mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-08 16:21:14 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67a16dabca | ||
|
|
59fc97fb28 | ||
|
|
7d44b60ae4 | ||
|
|
776ca6a3e4 | ||
|
|
ee26e94170 | ||
|
|
d401d4f837 | ||
|
|
18b70844d1 | ||
|
|
c8f2498681 | ||
|
|
30902561c0 | ||
|
|
3fc515f48d | ||
|
|
6cd5feed79 | ||
|
|
09530ef7c4 |
+36
-3
@@ -8,9 +8,10 @@ SCRIPT_PATH=$(dirname "$(readlink -f "${0}")")
|
||||
pushd "${SCRIPT_PATH}" > /dev/null
|
||||
|
||||
function usage() {
|
||||
echo "Usage: ./${SCRIPT_NAME} [-1][-b][-c][-d][-D][-e][-F][-g][-h][-i][-j N][-p][-r][-s][-t][-u][-l][-L]"
|
||||
echo "Usage: ./${SCRIPT_NAME} [-1][-b][-c][-d][-D][-e][-F][-g][-h][-i][-j N][-J N][-p][-r][-s][-t][-u][-l][-L]"
|
||||
echo " -1: limit builds to one core (where possible)"
|
||||
echo " -j N: limit builds to N cores (where possible)"
|
||||
echo " -J N: build up to N dependencies at a time, each still using -j jobs (default: 1)"
|
||||
echo " -b: build in Debug mode"
|
||||
echo " -c: force a clean build"
|
||||
echo " -C: enable ANSI-colored compile output (GNU/Clang only)"
|
||||
@@ -36,12 +37,13 @@ function usage() {
|
||||
}
|
||||
|
||||
SLIC3R_PRECOMPILED_HEADERS="ON"
|
||||
DEPS_PARALLEL=""
|
||||
|
||||
unset name
|
||||
BUILD_DIR=build
|
||||
BUILD_CONFIG=Release
|
||||
FORWARDED_ARGS=()
|
||||
while getopts ":1j:bcCdDeFghiprstulL" opt ; do
|
||||
while getopts ":1j:J:bcCdDeFghiprstulL" opt ; do
|
||||
case ${opt} in
|
||||
1 )
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=1
|
||||
@@ -51,6 +53,10 @@ while getopts ":1j:bcCdDeFghiprstulL" opt ; do
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=$OPTARG
|
||||
FORWARDED_ARGS+=("-j" "$OPTARG")
|
||||
;;
|
||||
J )
|
||||
DEPS_PARALLEL=$OPTARG
|
||||
FORWARDED_ARGS+=("-J" "$OPTARG")
|
||||
;;
|
||||
b )
|
||||
BUILD_DIR=build-dbg
|
||||
BUILD_CONFIG=Debug
|
||||
@@ -135,6 +141,11 @@ if [[ -n "${CLEAN_DOCKER_IMAGE}" ]] && [[ -z "${USE_DOCKER}" ]] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "${DEPS_PARALLEL}" ]] && ! [[ "${DEPS_PARALLEL}" =~ ^[1-9][0-9]*$ ]] ; then
|
||||
echo "Error: -J expects a positive integer."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function check_available_memory_and_disk() {
|
||||
FREE_MEM_GB=$(free --gibi --total | grep 'Mem' | rev | cut --delimiter=" " --fields=1 | rev)
|
||||
MIN_MEM_GB=10
|
||||
@@ -537,7 +548,29 @@ if [[ -n "${BUILD_DEPS}" ]] ; then
|
||||
fi
|
||||
|
||||
print_and_run cmake -S deps -B deps/$BUILD_DIR "${CMAKE_C_CXX_COMPILER_CLANG[@]}" "${CMAKE_LLD_LINKER_ARGS[@]}" "${CMAKE_CCACHE_ARGS[@]}" -G Ninja "${COLORED_OUTPUT}" "${BUILD_ARGS[@]}"
|
||||
print_and_run cmake --build deps/$BUILD_DIR -j1
|
||||
# The top-level build runs one dependency at a time by default, which keeps the console
|
||||
# output readable and lets that dependency's own build use all of CMAKE_BUILD_PARALLEL_LEVEL.
|
||||
# -J raises the top level instead, and -j still applies in full to every dependency, so the
|
||||
# worst case is -J times -j compile jobs at once. Ninja has no job server to share a pool
|
||||
# across the nested builds, so that ceiling is not enforced anywhere: pick -J to suit the RAM.
|
||||
DEPS_JOBS=1
|
||||
if [[ -n "${DEPS_PARALLEL}" ]] ; then
|
||||
DEPS_JOBS=${DEPS_PARALLEL}
|
||||
SAVED_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL-}
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-$(nproc)}
|
||||
echo "Building up to ${DEPS_JOBS} dependencies at a time, ${CMAKE_BUILD_PARALLEL_LEVEL} jobs each: up to $(( DEPS_JOBS * CMAKE_BUILD_PARALLEL_LEVEL )) compile jobs at once."
|
||||
fi
|
||||
|
||||
print_and_run cmake --build deps/$BUILD_DIR -j"${DEPS_JOBS}"
|
||||
|
||||
if [[ -n "${DEPS_PARALLEL}" ]] ; then
|
||||
# Give the whole -j back to the OrcaSlicer build below.
|
||||
if [[ -n "${SAVED_PARALLEL_LEVEL}" ]] ; then
|
||||
export CMAKE_BUILD_PARALLEL_LEVEL=${SAVED_PARALLEL_LEVEL}
|
||||
else
|
||||
unset CMAKE_BUILD_PARALLEL_LEVEL
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${BUILD_ORCA}" ]] || [[ -n "${BUILD_TESTS}" ]] ; then
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# Separated infills — High Level Design
|
||||
|
||||
## Purpose and scope
|
||||
|
||||
An object's infill patterns are laid out from one reference point, the center
|
||||
of the object. When an object groups several parts that do not touch, every
|
||||
part cuts the same object-wide pattern at a different place, so equal parts get
|
||||
different infill. `separated_infills` lays the infill of every connected body
|
||||
out from the center of that body instead, as if the body were sliced on its own.
|
||||
|
||||
The option covers sparse infill, internal solid infill and bridges. Top and
|
||||
bottom surfaces are left to `center_of_surface_pattern`, which centers the
|
||||
Archimedean Chords and Octagram Spiral surface patterns. The option is off by
|
||||
default; with it off, or for an object made of a single body, no fill changes.
|
||||
Adaptive Cubic and Support Cubic do not depend on the option: they always fill
|
||||
each body on its own (see Octree infill).
|
||||
|
||||
## Bodies
|
||||
|
||||
`PrintObject::prepare_infill()` groups the islands of every layer (`lslices`)
|
||||
into 3D connected bodies before bridges are detected, so bridge anchors and
|
||||
printed infill share one origin. Islands on adjacent layers belong to one body
|
||||
when their slices overlap. Parts that touch or overlap form one body. Separate
|
||||
parts, disconnected islands of one mesh, and interleaved parts that never touch,
|
||||
such as chain links, each form their own. Every island stores the index of its
|
||||
body in `Layer::lslices_separated_component_ids`, and
|
||||
`PrintObject::separated_body_bboxes()` holds the bounding box of each body over
|
||||
all its layers.
|
||||
|
||||
The pass runs when a region uses separated infills, per-model surface centering
|
||||
or an octree infill pattern. It is skipped when the object has one model part
|
||||
that cannot be split, since a single body already shares the object center.
|
||||
|
||||
## Centering a fill
|
||||
|
||||
`infill_body()` matches each fill region to the island it overlaps most, among
|
||||
the islands whose bounding boxes overlap it, and the filler takes the bounding
|
||||
box of that island's body instead of the object's. The box covers every layer
|
||||
of the body, which is the box the body gets when sliced alone, so patterns that
|
||||
depend on its extent as well as its center come out the same too. Bridge
|
||||
anchoring (`Layer::generate_sparse_infill_polylines_for_anchoring()`) makes the
|
||||
same choice, so the anchors match the printed infill.
|
||||
|
||||
The patterns follow the body's box in one of two ways:
|
||||
|
||||
- Rectilinear and its variants, Line, Grid, Triangles, Tri-hexagon, Cubic,
|
||||
Quarter Cubic, Lateral Lattice, Lateral Honeycomb and the plane-path patterns
|
||||
(Hilbert Curve, Archimedean Chords, Octagram Spiral) are laid out from the
|
||||
box: they phase their lines through its center, and Hilbert Curve and the Zig
|
||||
Zag links start from its corner. `Fill::extended_object_bounding_box()`
|
||||
extends the box about its center, so it also serves a box that is not
|
||||
centered on the origin.
|
||||
- Honeycomb, 3D Honeycomb, Cross Hatch, Gyroid, TPMS-D and TPMS-FK are laid out
|
||||
from the coordinate origin, which is the object center. They return true from
|
||||
`Fill::aligned_to_origin()`, and `Fill::fill_surface()` moves each region so
|
||||
that the box center lands on the origin, fills it, and moves the paths back.
|
||||
With the default box the center is the origin, so nothing moves.
|
||||
|
||||
`is_separable_infill_pattern()` lists these patterns. The settings show the
|
||||
option only when the sparse infill pattern is one of them.
|
||||
|
||||
## Octree infill
|
||||
|
||||
Adaptive Cubic and Support Cubic take their lines from an octree, laid out from
|
||||
the center of the mesh it is built from and refined near its surfaces. An
|
||||
octree of the whole object would lay every part out from the object's center
|
||||
and refine it near the other parts, so these patterns
|
||||
(`is_octree_infill_pattern()`) always fill each body on its own, and the
|
||||
settings hide the option for them.
|
||||
|
||||
For an object of several bodies, `PrintObject::prepare_adaptive_infill_data()`
|
||||
builds one octree per body (`FillAdaptive::Octrees`) from the triangles of that
|
||||
body only, which is the octree the body gets when sliced alone. Each connected
|
||||
component of the mesh goes to the body that most of a few sampled triangles lie
|
||||
on. A sample is taken a layer height inside the solid, behind the triangle, and
|
||||
looked up in the islands of the nearest layer. Each internal bridge surface goes
|
||||
to the body of its island. The fill takes the octree of the region's body, from
|
||||
the same `infill_body()`. The octree of the whole object is built only for an
|
||||
object of a single body, or when some body received no triangles, which then
|
||||
uses it.
|
||||
|
||||
## Patterns left out
|
||||
|
||||
Lightning grows its trees over the whole object, so moving a reference point
|
||||
cannot center it on one body. Concentric and Spiral Inset follow the outline of
|
||||
each region and need no centering.
|
||||
|
||||
Solid infill at full density spaces its lines over the extent of each region,
|
||||
so it is already independent of the other bodies. Only bridges, which keep
|
||||
their line spacing, and the plane-path solid patterns depend on the center.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Elegoo",
|
||||
"version": "02.04.00.12",
|
||||
"version": "02.04.00.11",
|
||||
"force_update": "0",
|
||||
"description": "Elegoo configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
"Elegoo"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; Elegoo PLA filament start gcode\n"
|
||||
"; filament start gcode\n"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Orca Arena Printer",
|
||||
"url": "",
|
||||
"version": "02.04.00.08",
|
||||
"version": "02.04.00.07",
|
||||
"force_update": "0",
|
||||
"description": "Orca Arena configuration files",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "OrcaFilamentLibrary",
|
||||
"version": "02.04.00.19",
|
||||
"version": "02.04.00.15",
|
||||
"force_update": "0",
|
||||
"description": "Orca Filament Library",
|
||||
"filament_list": [
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"60"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; Elegoo PLA filament start gcode\n"
|
||||
"; filament start gcode\n"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n"
|
||||
|
||||
@@ -4094,6 +4094,8 @@ int CLI::run(int argc, char **argv)
|
||||
flush_and_exit(CLI_MIXED_FILAMENT_INVALID);
|
||||
}
|
||||
}
|
||||
if (filament_count > 0)
|
||||
resize_mixed_filament_metadata(m_print_config, size_t(filament_count), size_t(filament_count));
|
||||
|
||||
m_print_config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value = printer_technology;
|
||||
|
||||
|
||||
@@ -84,7 +84,6 @@
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <gp_XY.hxx>
|
||||
#include <initializer_list>
|
||||
#include <math.h>
|
||||
|
||||
@@ -261,8 +261,6 @@ set(lisbslic3r_sources
|
||||
GCode/PreciseSeam.cpp
|
||||
GCode/PreciseSeam.hpp
|
||||
GCode/PreciseSeamInternal.hpp
|
||||
#GCodeSender.cpp
|
||||
#GCodeSender.hpp
|
||||
GCode/SmallAreaInfillFlowCompensator.cpp
|
||||
GCode/SmallAreaInfillFlowCompensator.hpp
|
||||
GCode/SpiralVase.cpp
|
||||
@@ -447,7 +445,6 @@ set(lisbslic3r_sources
|
||||
SLA/SupportTreeBuildsteps.hpp
|
||||
SLA/SupportTree.cpp
|
||||
SLA/SupportTree.hpp
|
||||
#SLA/SupportTreeIGL.cpp
|
||||
SLA/SupportTreeMesher.cpp
|
||||
SLA/SupportTreeMesher.hpp
|
||||
SlicesToTriangleMesh.cpp
|
||||
|
||||
@@ -2589,7 +2589,6 @@ void priv::create_face_types(FaceTypeMap &map,
|
||||
}
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/clip.h>
|
||||
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
bool priv::clip_cut(SurfacePatch &cut, CutMesh clipper)
|
||||
{
|
||||
CutMesh& tm = cut.mesh;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "Emboss.hpp"
|
||||
#include <optional>
|
||||
#include <stdio.h>
|
||||
#include <numeric>
|
||||
#include <cstdlib>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
+31
-25
@@ -29,6 +29,7 @@
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include "Fill.hpp"
|
||||
#include "libslic3r/Fill/FillBase.hpp"
|
||||
#include "FillAdaptive.hpp"
|
||||
#include "FillRectilinear.hpp"
|
||||
#include "FillLightning.hpp"
|
||||
#include "FillConcentricInternal.hpp"
|
||||
@@ -926,7 +927,6 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
params.lateral_lattice_angle_2 = region_config.lateral_lattice_angle_2;
|
||||
params.infill_overhang_angle = region_config.infill_overhang_angle;
|
||||
params.center_of_surface_pattern = region_config.center_of_surface_pattern;
|
||||
params.separated_infills = region_config.separated_infills;
|
||||
if (params.pattern == ipLockedZag) {
|
||||
params.infill_lock_depth = scale_(region_config.infill_lock_depth);
|
||||
params.skin_infill_depth = scale_(region_config.skin_infill_depth);
|
||||
@@ -999,6 +999,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
// (which would unnecessarily split fill batching).
|
||||
// Stored on SurfaceFillParams; copied to FillParams during conversion.
|
||||
params.gyroid_optimized = (params.pattern == ipGyroid) && region_config.gyroid_optimized;
|
||||
// Orca: Likewise separated_infills only where it can move the pattern.
|
||||
params.separated_infills = region_config.separated_infills && is_separable_infill_pattern(params.pattern) &&
|
||||
params.extrusion_role != erTopSolidInfill && params.extrusion_role != erBottomSurface;
|
||||
|
||||
if (params.extrusion_role == erInternalInfill) {
|
||||
params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.infill_direction.value,
|
||||
@@ -1073,7 +1076,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
//get locked region param
|
||||
if (params.pattern == ipLockedZag){
|
||||
const PrintObject *object = layerm.layer()->object();
|
||||
auto nozzle_diameter = float(object->print()->config().nozzle_diameter.get_at(layerm.region().extruder(extrusion_role) - 1));
|
||||
auto nozzle_diameter = float(nozzle_diameter_for_filament(object->print()->config(), layerm.region().extruder(extrusion_role), object->print()->is_BBL_printer()));
|
||||
Flow skin_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skin_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness));
|
||||
//add skin flow
|
||||
append_flow_param(lock_param.skin_flow_params, skin_flow, surface.expolygon);
|
||||
@@ -1271,29 +1274,28 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
|
||||
// Orca: Anchors and printed infill must share the same body origin. Keep the choice
|
||||
// here so per-model surface centering and separated sparse infill cannot drift apart.
|
||||
static BoundingBox infill_bounding_box(const Layer &layer, const SurfaceFill &fill, const ExPolygon &expoly, BoundingBox bbox)
|
||||
// Returns the connected body the fill region is laid out on, or -1 to keep the object's origin.
|
||||
static int infill_body(const Layer &layer, const SurfaceFill &fill, const ExPolygon &expoly)
|
||||
{
|
||||
const auto ¶ms = fill.params;
|
||||
const auto &config = layer.regions()[fill.region_id]->region().config();
|
||||
const bool external = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface;
|
||||
const bool per_model = external && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model &&
|
||||
const bool per_model = (params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface) &&
|
||||
params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model &&
|
||||
(params.pattern == ipArchimedeanChords || params.pattern == ipOctagramSpiral);
|
||||
const bool separate = !external && params.separated_infills &&
|
||||
(is_separable_infill_pattern(params.pattern) || !config.solid_infill_rotate_template.value.empty() ||
|
||||
!config.sparse_infill_rotate_template.value.empty());
|
||||
if (per_model || separate) {
|
||||
double best_overlap = 0.;
|
||||
for (size_t i = 0; i < layer.lslices.size() && i < layer.lslices_separated_component_bboxes.size(); ++i) {
|
||||
int body = -1;
|
||||
if (per_model || params.separated_infills || is_octree_infill_pattern(params.pattern)) {
|
||||
const BoundingBox box = get_extents(expoly);
|
||||
double best_overlap = 0.;
|
||||
for (size_t i = 0; i < layer.lslices.size() && i < layer.lslices_separated_component_ids.size(); ++i) {
|
||||
if (! layer.lslices_bboxes[i].overlap(box))
|
||||
continue;
|
||||
const double overlap = area(intersection_ex(layer.lslices[i], expoly));
|
||||
if (overlap > best_overlap) {
|
||||
best_overlap = overlap;
|
||||
const Point center = layer.lslices_separated_component_bboxes[i].center();
|
||||
bbox = layer.object()->bounding_box();
|
||||
bbox.translate(center.x(), center.y());
|
||||
body = int(layer.lslices_separated_component_ids[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bbox;
|
||||
return body;
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
@@ -1318,7 +1320,7 @@ void export_group_fills_to_svg(const char *path, const std::vector<SurfaceFill>
|
||||
#endif
|
||||
|
||||
// friend to Layer
|
||||
void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator)
|
||||
void Layer::make_fills(const FillAdaptive::Octrees* adaptive_fill_octrees, const FillAdaptive::Octrees* support_fill_octrees, FillLightning::Generator* lightning_generator)
|
||||
{
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
layerm->fills.clear();
|
||||
@@ -1351,7 +1353,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
f->z = this->print_z;
|
||||
f->angle = surface_fill.params.angle;
|
||||
f->fixed_angle = surface_fill.params.fixed_angle;
|
||||
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
|
||||
const FillAdaptive::Octrees *octrees = surface_fill.params.pattern == ipSupportCubic ? support_fill_octrees : adaptive_fill_octrees;
|
||||
f->print_config = &this->object()->print()->config();
|
||||
f->print_object_config = &this->object()->config();
|
||||
if (surface_fill.params.pattern == ipConcentricInternal) {
|
||||
@@ -1443,8 +1445,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
params.can_reverse = false;
|
||||
for (ExPolygon& expoly : surface_fill.expolygons) {
|
||||
|
||||
// Orca: Reuse the body origin used for bridge anchoring, resetting it for each surface.
|
||||
f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox));
|
||||
// Orca: Reuse the body box and octree used for bridge anchoring, resetting them for each surface.
|
||||
const int body = infill_body(*this, surface_fill, expoly);
|
||||
f->set_bounding_box(body >= 0 ? this->object()->separated_body_bboxes()[body] : bbox);
|
||||
f->adapt_fill_octree = octrees ? octrees->get(body) : nullptr;
|
||||
|
||||
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
|
||||
if (params.symmetric_infill_y_axis) {
|
||||
@@ -1512,7 +1516,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
* - For lightning/adaptive patterns, the respective generators are wired so their
|
||||
* polylines match the final infill layout.
|
||||
*/
|
||||
Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) const
|
||||
Polylines Layer::generate_sparse_infill_polylines_for_anchoring(const FillAdaptive::Octrees* adaptive_fill_octrees, const FillAdaptive::Octrees* support_fill_octrees, FillLightning::Generator* lightning_generator) const
|
||||
{
|
||||
LockRegionParam skin_inner_param;
|
||||
std::vector<SurfaceFill> surface_fills = group_fills(*this, skin_inner_param);
|
||||
@@ -1570,7 +1574,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
||||
f->z = this->print_z;
|
||||
f->angle = surface_fill.params.angle;
|
||||
f->fixed_angle = surface_fill.params.fixed_angle;
|
||||
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
|
||||
const FillAdaptive::Octrees *octrees = surface_fill.params.pattern == ipSupportCubic ? support_fill_octrees : adaptive_fill_octrees;
|
||||
f->print_config = &this->object()->print()->config();
|
||||
f->print_object_config = &this->object()->config();
|
||||
|
||||
@@ -1617,8 +1621,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
||||
params.extrusion_role = surface_fill.params.extrusion_role;
|
||||
|
||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||
// Orca: Match the per-body origin of make_fills() before generating physical anchors.
|
||||
f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox));
|
||||
// Orca: Match the per-body box and octree of make_fills() before generating physical anchors.
|
||||
const int body = infill_body(*this, surface_fill, expoly);
|
||||
f->set_bounding_box(body >= 0 ? this->object()->separated_body_bboxes()[body] : bbox);
|
||||
f->adapt_fill_octree = octrees ? octrees->get(body) : nullptr;
|
||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||
f->spacing = surface_fill.params.spacing;
|
||||
surface_fill.surface.expolygon = std::move(expoly);
|
||||
@@ -1788,7 +1794,7 @@ void Layer::make_ironing()
|
||||
|
||||
// Create the ironing extrusions for regions <i, j)
|
||||
ExPolygons ironing_areas;
|
||||
double nozzle_dmr = this->object()->print()->config().nozzle_diameter.get_at(ironing_params.extruder - 1);
|
||||
double nozzle_dmr = nozzle_diameter_for_filament(this->object()->print()->config(), ironing_params.extruder, this->object()->print()->is_BBL_printer());
|
||||
if (ironing_params.just_infill) {
|
||||
//TODO just_infill is currently not used.
|
||||
// Just infill.
|
||||
|
||||
@@ -25,6 +25,7 @@ public:
|
||||
// pattern is placed on top of previous layers
|
||||
bool use_bridge_flow() const override { return false; }
|
||||
bool is_self_crossing() override { return false; }
|
||||
bool aligned_to_origin() const override { return true; }
|
||||
|
||||
protected:
|
||||
void _fill_surface_single(
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
#include "FillBase.hpp"
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <Eigen/Geometry>
|
||||
@@ -37,6 +38,27 @@ struct Octree;
|
||||
struct OctreeDeleter { void operator()(Octree *p); };
|
||||
using OctreePtr = std::unique_ptr<Octree, OctreeDeleter>;
|
||||
|
||||
// Orca: One octree per body (see Layer::lslices_separated_component_ids), and one of the whole object
|
||||
// for objects of a single body or with a body that has none of its own.
|
||||
struct Octrees
|
||||
{
|
||||
OctreePtr object;
|
||||
std::vector<OctreePtr> bodies;
|
||||
|
||||
// A body without an octree, or body -1, uses the object's, or any body's when the object has none.
|
||||
Octree *get(int body) const
|
||||
{
|
||||
if (body >= 0 && size_t(body) < bodies.size() && bodies[body])
|
||||
return bodies[body].get();
|
||||
if (object)
|
||||
return object.get();
|
||||
for (const OctreePtr &octree : bodies)
|
||||
if (octree)
|
||||
return octree.get();
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate line spacing for
|
||||
// 1) adaptive cubic infill
|
||||
// 2) adaptive internal support cubic infill
|
||||
|
||||
@@ -128,6 +128,9 @@ Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
// Perform offset.
|
||||
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing)));
|
||||
// Orca: Separated infills move the box center onto each body; origin-aligned patterns follow it.
|
||||
const Point shift = this->aligned_to_origin() && ! empty(this->bounding_box) ? this->bounding_box.center() : Point::Zero();
|
||||
translate(expp, -shift);
|
||||
// Create the infills for each of the regions.
|
||||
Polylines polylines_out;
|
||||
for (size_t i = 0; i < expp.size(); ++ i)
|
||||
@@ -137,6 +140,8 @@ Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
_infill_direction(surface),
|
||||
std::move(expp[i]),
|
||||
polylines_out);
|
||||
for (Polyline &pl : polylines_out)
|
||||
pl.translate(shift);
|
||||
return polylines_out;
|
||||
}
|
||||
|
||||
@@ -1591,13 +1596,18 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
|
||||
// The extended bounding box of the whole object that covers any rotation of every layer.
|
||||
BoundingBox Fill::extended_object_bounding_box() const
|
||||
{
|
||||
BoundingBox out = bounding_box;
|
||||
// Orca: Extend about the box center, which separated infills move off the origin.
|
||||
const Point c = this->bounding_box.center();
|
||||
BoundingBox out = this->bounding_box;
|
||||
out.translate(-c.x(), -c.y());
|
||||
out.merge(Point(out.min.y(), out.min.x()));
|
||||
out.merge(Point(out.max.y(), out.max.x()));
|
||||
|
||||
// The bounding box is scaled by sqrt(2.) to ensure that the bounding box
|
||||
// covers any possible rotations.
|
||||
return out.scaled(sqrt(2.));
|
||||
out = out.scaled(sqrt(2.));
|
||||
out.translate(c.x(), c.y());
|
||||
return out;
|
||||
}
|
||||
|
||||
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms)
|
||||
|
||||
@@ -188,6 +188,9 @@ public:
|
||||
// Return true if infill has a consistent pattern between layers.
|
||||
virtual bool has_consistent_pattern() const { return false; }
|
||||
|
||||
// Orca: Is the pattern laid out from the origin instead of the bounding box center?
|
||||
virtual bool aligned_to_origin() const { return false; }
|
||||
|
||||
// Perform the fill.
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
virtual ThickPolylines fill_surface_arachne(const Surface* surface, const FillParams& params);
|
||||
|
||||
@@ -19,6 +19,7 @@ public:
|
||||
Fill *clone() const override { return new FillCrossHatch(*this); };
|
||||
~FillCrossHatch() override {}
|
||||
bool is_self_crossing() override { return false; }
|
||||
bool aligned_to_origin() const override { return true; }
|
||||
|
||||
protected:
|
||||
void _fill_surface_single(
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
bool use_bridge_flow() const override { return false; }
|
||||
bool is_self_crossing() override { return false; }
|
||||
bool aligned_to_origin() const override { return true; }
|
||||
|
||||
// Correction applied to regular infill angle to maximize printing
|
||||
// speed in default configuration (degrees)
|
||||
|
||||
@@ -20,6 +20,7 @@ class FillHoneycomb : public Fill
|
||||
public:
|
||||
~FillHoneycomb() override {}
|
||||
bool is_self_crossing() override { return false; }
|
||||
bool aligned_to_origin() const override { return true; }
|
||||
|
||||
protected:
|
||||
Fill* clone() const override { return new FillHoneycomb(*this); };
|
||||
|
||||
@@ -2750,23 +2750,6 @@ static void polylines_from_paths(const std::vector<MonotonicRegionLink> &path, c
|
||||
}
|
||||
}
|
||||
|
||||
// The extended bounding box of the whole object that covers any rotation of every layer.
|
||||
BoundingBox FillRectilinear::extended_object_bounding_box() const {
|
||||
// Build the extension around the box center. The transpose merge and the sqrt(2.) scaling
|
||||
// (which covers any possible rotation) are both defined about the origin, so a box that is not
|
||||
// origin-centered — e.g. a separated-infill box re-centered on a single assembly part — would be
|
||||
// distorted. Shift to the origin first and back afterwards; for the default origin-centered box
|
||||
// the two translations cancel and this is identical to the original behavior.
|
||||
const Point c = this->bounding_box.center();
|
||||
BoundingBox out = this->bounding_box;
|
||||
out.translate(-c.x(), -c.y());
|
||||
out.merge(Point(out.min.y(), out.min.x()));
|
||||
out.merge(Point(out.max.y(), out.max.x()));
|
||||
out = out.scaled(sqrt(2.));
|
||||
out.translate(c.x(), c.y());
|
||||
return out;
|
||||
}
|
||||
|
||||
bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out)
|
||||
{
|
||||
// At the end, only the new polylines will be rotated back.
|
||||
@@ -2801,7 +2784,13 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
// For infill that needs to be consistent between layers (like Zig Zag),
|
||||
// we use bounding box of whole object to match vertical lines between layers.
|
||||
BoundingBox bounding_box_src = poly_with_offset.bounding_box_src();
|
||||
BoundingBox bounding_box = this->has_consistent_pattern() ? this->extended_object_bounding_box() : bounding_box_src;
|
||||
BoundingBox bounding_box = bounding_box_src;
|
||||
if (this->has_consistent_pattern()) {
|
||||
// Orca: The polygons are rotated about the origin, so follow the box center to where it was rotated.
|
||||
const Point c = this->bounding_box.center();
|
||||
bounding_box = this->extended_object_bounding_box();
|
||||
bounding_box.translate(c.rotated(- rotate_vector.first) - c);
|
||||
}
|
||||
|
||||
// define flow spacing according to requested density
|
||||
if (params.full_infill() && !params.dont_adjust) {
|
||||
|
||||
@@ -42,9 +42,6 @@ protected:
|
||||
};
|
||||
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out);
|
||||
bool fill_surface_trapezoidal(const Surface *surface, FillParams params, Polylines &polylines_out, int Pattern_type);
|
||||
|
||||
// The extended bounding box of the whole object that covers any rotation of every layer.
|
||||
BoundingBox extended_object_bounding_box() const;
|
||||
};
|
||||
|
||||
class FillAlignedRectilinear : public FillRectilinear
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
Polylines& polylines_out) override;
|
||||
|
||||
bool is_self_crossing() override { return false; }
|
||||
bool aligned_to_origin() const override { return true; }
|
||||
|
||||
// Density adjustment to have a good %of weight.
|
||||
static constexpr double DensityAdjust = 2.1;
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
Polylines& polylines_out) override;
|
||||
|
||||
bool is_self_crossing() override { return false; }
|
||||
bool aligned_to_origin() const override { return true; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -241,14 +241,14 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
|
||||
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
|
||||
(object->config().support_line_width.value > 0) ? object->config().support_line_width : object->config().line_width,
|
||||
// if object->config().support_filament == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
|
||||
float(object->print()->config().nozzle_diameter.get_at(object->config().support_filament-1)),
|
||||
float(nozzle_diameter_for_filament(object->print()->config(), object->config().support_filament, object->print()->is_BBL_printer())),
|
||||
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value));
|
||||
}
|
||||
//BBS
|
||||
Flow support_transition_flow(const PrintObject* object)
|
||||
{
|
||||
//BBS: support transition of tree support is bridge flow
|
||||
float dmr = float(object->print()->config().nozzle_diameter.get_at(object->config().support_filament - 1));
|
||||
float dmr = float(nozzle_diameter_for_filament(object->print()->config(), object->config().support_filament, object->print()->is_BBL_printer()));
|
||||
return Flow::bridging_flow(dmr, dmr);
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
|
||||
frSupportMaterial,
|
||||
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
|
||||
(width.value > 0) ? width : object->config().line_width,
|
||||
float(print_config.nozzle_diameter.get_at(object->config().support_filament-1)),
|
||||
float(nozzle_diameter_for_filament(print_config, object->config().support_filament, object->print()->is_BBL_printer())),
|
||||
(layer_height > 0.f) ? layer_height : float(print_config.initial_layer_print_height.value));
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
|
||||
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
|
||||
(object->config().support_line_width > 0) ? object->config().support_line_width : object->config().line_width,
|
||||
// if object->config().support_interface_filament == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
|
||||
float(object->print()->config().nozzle_diameter.get_at(object->config().support_interface_filament-1)),
|
||||
float(nozzle_diameter_for_filament(object->print()->config(), object->config().support_interface_filament, object->print()->is_BBL_printer())),
|
||||
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value));
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
#include "TDataStd_Name.hxx"
|
||||
#include "BRepBuilderAPI_Transform.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "BRep_Tool.hxx"
|
||||
#include "BRepTools.hxx"
|
||||
#include <IMeshTools_Parameters.hxx>
|
||||
|
||||
+41
-65
@@ -43,7 +43,6 @@
|
||||
#include "Print.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "Time.hpp"
|
||||
@@ -155,14 +154,9 @@ static const float g_purge_volume_one_time = 135.f;
|
||||
static const int g_max_flush_count = 4;
|
||||
static const size_t g_max_label_object = 64;
|
||||
|
||||
static bool is_bambu_x2d_printer(const FullPrintConfig &config)
|
||||
{
|
||||
return config.printer_model.value == "Bambu Lab X2D";
|
||||
}
|
||||
|
||||
// Multi-nozzle printer predicate: an extruder carries a nozzle cluster (extruder_max_nozzle_count
|
||||
// entry > 1). Today only H2C profiles trip it, so every existing single- and dual-extruder printer
|
||||
// is excluded and keeps its historic placeholder values.
|
||||
// is excluded and keeps its historic first-filament marker.
|
||||
static bool is_multi_nozzle_printer(const FullPrintConfig &config)
|
||||
{
|
||||
return std::any_of(config.extruder_max_nozzle_count.values.begin(),
|
||||
@@ -170,33 +164,20 @@ static bool is_multi_nozzle_printer(const FullPrintConfig &config)
|
||||
[](int v) { return v > 1; });
|
||||
}
|
||||
|
||||
static int hotend_id_for_gcode_placeholder(const FullPrintConfig &config, int hotend_id)
|
||||
// current_hotend / next_hotend value. On a BBL printer: the real nozzle id only while the print uses a
|
||||
// dynamic nozzle map (a filament moves between nozzles across layers), else -1. Bambu firmware reads an
|
||||
// explicit hotend index as a request for the Filament Track Switch and rejects the job on a printer
|
||||
// without one. group_result may be null on slicing paths that don't populate it, which resolves to -1.
|
||||
// Any other printer has no firmware hotend selection and gets the filament's extruder index, whatever
|
||||
// its nozzle map, so existing custom G-code keeps its values.
|
||||
static int hotend_id_for_gcode_placeholder(const std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> &group_result,
|
||||
bool is_bbl_printer, int filament_id, int extruder_id, int layer_id = -1)
|
||||
{
|
||||
return is_bambu_x2d_printer(config) ? -1 : hotend_id;
|
||||
}
|
||||
|
||||
// current_hotend / next_hotend value. For multi-nozzle printers a dynamic nozzle map yields the real
|
||||
// nozzle id, a static map yields -1:
|
||||
// - multi-nozzle (H2C): dynamic nozzle map -> real nozzle id; static -> -1.
|
||||
// The dynamic branch is dormant today: the selector create() overload that sets the flag has no
|
||||
// callers yet (deferred with the nozzle-assignment pipeline), so H2C currently resolves to -1.
|
||||
// - X2D: keeps its historic -1 (single-nozzle -> falls through to the fallback helper).
|
||||
// - every other (existing single-nozzle) printer: keeps its historic extruder-id value, so
|
||||
// existing g-code stays byte-identical.
|
||||
// group_result may be null on slicing paths that don't populate it -> the dynamic branch is simply
|
||||
// skipped, so we never dereference null.
|
||||
static int hotend_id_for_gcode_placeholder(const FullPrintConfig &config,
|
||||
const std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> &group_result,
|
||||
int filament_id,
|
||||
int extruder_id,
|
||||
int layer_id = -1)
|
||||
{
|
||||
if (is_multi_nozzle_printer(config)) {
|
||||
if (group_result && group_result->is_support_dynamic_nozzle_map() && filament_id >= 0)
|
||||
return group_result->get_nozzle_id(filament_id, layer_id);
|
||||
return -1;
|
||||
}
|
||||
return hotend_id_for_gcode_placeholder(config, extruder_id);
|
||||
if (!is_bbl_printer)
|
||||
return extruder_id;
|
||||
if (group_result && group_result->is_support_dynamic_nozzle_map() && filament_id >= 0)
|
||||
return group_result->get_nozzle_id(filament_id, layer_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Logical nozzle id for the *_nozzle_id placeholders. Null-safe: falls back to the
|
||||
@@ -211,24 +192,20 @@ static int nozzle_id_for_gcode_placeholder(const std::shared_ptr<MultiNozzleUtil
|
||||
|
||||
// Init variants: the start-gcode init sites (first_non_support_hotend / initial_no_support_hotend /
|
||||
// current_hotend / initial_nozzle_id / filament_start current_nozzle_id) use get_first_nozzle_for_filament
|
||||
// (the nozzle a filament FIRST uses) rather than the layer-based get_nozzle_id. Same hotend-value semantics
|
||||
// as hotend_id_for_gcode_placeholder above (multi-nozzle static -> -1; dynamic branch dormant;
|
||||
// existing printers -> extruder id; X2D -> -1); they differ from the layer-based helper only on the dormant
|
||||
// dynamic path for a filament first used after layer 0.
|
||||
static int first_hotend_id_for_gcode_placeholder(const FullPrintConfig &config,
|
||||
const std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> &group_result,
|
||||
int filament_id,
|
||||
int extruder_id)
|
||||
// (the nozzle a filament FIRST uses) rather than the layer-based get_nozzle_id. Same hotend-value rule
|
||||
// as hotend_id_for_gcode_placeholder above; they differ from the layer-based helper only on the dynamic
|
||||
// path for a filament first used after layer 0.
|
||||
static int first_hotend_id_for_gcode_placeholder(const std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> &group_result,
|
||||
bool is_bbl_printer, int filament_id, int extruder_id)
|
||||
{
|
||||
if (is_multi_nozzle_printer(config)) {
|
||||
if (group_result && group_result->is_support_dynamic_nozzle_map() && filament_id >= 0) {
|
||||
auto nozzle = group_result->get_first_nozzle_for_filament(filament_id);
|
||||
if (nozzle)
|
||||
return nozzle->group_id;
|
||||
}
|
||||
return -1;
|
||||
if (!is_bbl_printer)
|
||||
return extruder_id;
|
||||
if (group_result && group_result->is_support_dynamic_nozzle_map() && filament_id >= 0) {
|
||||
auto nozzle = group_result->get_first_nozzle_for_filament(filament_id);
|
||||
if (nozzle)
|
||||
return nozzle->group_id;
|
||||
}
|
||||
return hotend_id_for_gcode_placeholder(config, extruder_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int first_nozzle_id_for_gcode_placeholder(const std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> &group_result,
|
||||
@@ -1215,12 +1192,11 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
|
||||
config.set_key_value("previous_extruder", new ConfigOptionInt(old_filament_id));
|
||||
config.set_key_value("next_extruder", new ConfigOptionInt(new_filament_id));
|
||||
// current_hotend/next_hotend (see hotend_id_for_gcode_placeholder): multi-nozzle H2C -> -1
|
||||
// (static; dynamic branch dormant), X2D -> -1, existing printers -> extruder id.
|
||||
const bool is_bbl_printer = gcodegen.m_print->is_BBL_printer();
|
||||
config.set_key_value("current_hotend", new ConfigOptionInt(
|
||||
hotend_id_for_gcode_placeholder(gcodegen.m_config, group_result, old_filament_id, old_extruder_id, m_layer_idx)));
|
||||
hotend_id_for_gcode_placeholder(group_result, is_bbl_printer, old_filament_id, old_extruder_id, m_layer_idx)));
|
||||
config.set_key_value("next_hotend", new ConfigOptionInt(
|
||||
hotend_id_for_gcode_placeholder(gcodegen.m_config, group_result, new_filament_id, (int) gcodegen.get_extruder_id(new_filament_id), m_layer_idx)));
|
||||
hotend_id_for_gcode_placeholder(group_result, is_bbl_printer, new_filament_id, (int) gcodegen.get_extruder_id(new_filament_id), m_layer_idx)));
|
||||
config.set_key_value("current_nozzle_id", new ConfigOptionInt(old_nozzle_id));
|
||||
config.set_key_value("next_nozzle_id", new ConfigOptionInt(next_nozzle_id));
|
||||
config.set_key_value("current_filament_id", new ConfigOptionInt(old_filament_id));
|
||||
@@ -1499,7 +1475,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
gcodegen.placeholder_parser().set("current_nozzle_id",
|
||||
nozzle_id_for_gcode_placeholder(group_result, new_filament_id, new_extruder_id, m_layer_idx));
|
||||
gcodegen.placeholder_parser().set("current_hotend",
|
||||
hotend_id_for_gcode_placeholder(gcodegen.m_config, group_result, new_filament_id, new_extruder_id, m_layer_idx));
|
||||
hotend_id_for_gcode_placeholder(group_result, gcodegen.m_print->is_BBL_printer(), new_filament_id, new_extruder_id, m_layer_idx));
|
||||
{
|
||||
size_t fi = gcodegen.get_filament_config_index(new_filament_id);
|
||||
gcodegen.placeholder_parser().set("retraction_distance_when_cut", gcodegen.m_config.retraction_distances_when_cut.get_at(fi));
|
||||
@@ -3310,20 +3286,19 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
first_non_support_hotends.reserve(first_non_support_filaments.size());
|
||||
for (int filament_id : first_non_support_filaments)
|
||||
first_non_support_hotends.push_back(filament_id < 0 ? -1 :
|
||||
first_hotend_id_for_gcode_placeholder(m_config, group_result, filament_id, (int) get_extruder_id(filament_id)));
|
||||
first_hotend_id_for_gcode_placeholder(group_result, is_bbl_printers, filament_id, (int) get_extruder_id(filament_id)));
|
||||
|
||||
this->placeholder_parser().set("first_non_support_tools", new ConfigOptionInts(first_non_support_filaments));
|
||||
this->placeholder_parser().set("first_non_support_filaments", new ConfigOptionInts(first_non_support_filaments));
|
||||
this->placeholder_parser().set("first_non_support_hotend", new ConfigOptionInts(first_non_support_hotends));
|
||||
this->placeholder_parser().set("initial_no_support_tool", initial_non_support_extruder_id);
|
||||
this->placeholder_parser().set("initial_no_support_extruder", initial_non_support_extruder_id);
|
||||
// initial_no_support_hotend/current_hotend (see first_hotend_id_for_gcode_placeholder): multi-nozzle
|
||||
// H2C -> -1 (static; dynamic branch dormant), X2D -> -1, existing printers -> extruder id.
|
||||
this->placeholder_parser().set("initial_no_support_hotend",
|
||||
first_hotend_id_for_gcode_placeholder(m_config, group_result, (int) initial_non_support_extruder_id, (int) get_extruder_id(initial_non_support_extruder_id)));
|
||||
first_hotend_id_for_gcode_placeholder(group_result, is_bbl_printers, (int) initial_non_support_extruder_id,
|
||||
(int) get_extruder_id(initial_non_support_extruder_id)));
|
||||
this->placeholder_parser().set("current_extruder", initial_extruder_id);
|
||||
this->placeholder_parser().set("current_hotend",
|
||||
first_hotend_id_for_gcode_placeholder(m_config, group_result, (int) initial_extruder_id, extruder_id));
|
||||
first_hotend_id_for_gcode_placeholder(group_result, is_bbl_printers, (int) initial_extruder_id, extruder_id));
|
||||
this->placeholder_parser().set("current_filament_id", (int) initial_extruder_id);
|
||||
this->placeholder_parser().set("current_extruder_id", extruder_id);
|
||||
this->placeholder_parser().set("current_nozzle_id",
|
||||
@@ -3832,7 +3807,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
file.writeln(filament_start_gcode);
|
||||
// Mark the first filament used in print. Multi-nozzle printers (H2C) get ";VT%d H%d" where
|
||||
// H = dynamic ? nozzle_id : -1; existing single-nozzle printers keep the bare ";VT%d" so their
|
||||
// g-code stays byte-identical. (The dynamic branch is dormant, so H2C currently emits H-1.)
|
||||
// g-code stays byte-identical.
|
||||
if (is_multi_nozzle_printer(m_config)) {
|
||||
int initial_nozzle_id = -1;
|
||||
if (group_result && group_result->is_support_dynamic_nozzle_map()) {
|
||||
@@ -9616,6 +9591,9 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
this->placeholder_parser().set("current_extruder_id", new_extruder_id);
|
||||
this->placeholder_parser().set("current_nozzle_id",
|
||||
nozzle_id_for_gcode_placeholder(m_print->get_layered_nozzle_group_result(), (int) new_filament_id, new_extruder_id, m_layer_index));
|
||||
this->placeholder_parser().set("current_hotend",
|
||||
hotend_id_for_gcode_placeholder(m_print->get_layered_nozzle_group_result(), m_print->is_BBL_printer(), (int) new_filament_id,
|
||||
new_extruder_id, m_layer_index));
|
||||
{
|
||||
size_t fi = get_filament_config_index(new_filament_id);
|
||||
this->placeholder_parser().set("retraction_distance_when_ec", m_config.retraction_distances_when_ec.get_at(fi));
|
||||
@@ -9794,12 +9772,10 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
dyn_config.set_key_value("outer_wall_volumetric_speed", new ConfigOptionFloat(outer_wall_volumetric_speed));
|
||||
dyn_config.set_key_value("previous_extruder", new ConfigOptionInt(old_filament_id));
|
||||
dyn_config.set_key_value("next_extruder", new ConfigOptionInt((int)new_filament_id));
|
||||
// current_hotend/next_hotend (see hotend_id_for_gcode_placeholder): multi-nozzle H2C -> -1
|
||||
// (static; dynamic branch dormant), X2D -> -1, existing printers -> extruder id.
|
||||
dyn_config.set_key_value("current_hotend", new ConfigOptionInt(
|
||||
hotend_id_for_gcode_placeholder(m_config, group_result, old_filament_id, old_extruder_id, m_layer_index)));
|
||||
hotend_id_for_gcode_placeholder(group_result, m_print->is_BBL_printer(), old_filament_id, old_extruder_id, m_layer_index)));
|
||||
dyn_config.set_key_value("next_hotend", new ConfigOptionInt(
|
||||
hotend_id_for_gcode_placeholder(m_config, group_result, (int) new_filament_id, new_extruder_id, m_layer_index)));
|
||||
hotend_id_for_gcode_placeholder(group_result, m_print->is_BBL_printer(), (int) new_filament_id, new_extruder_id, m_layer_index)));
|
||||
dyn_config.set_key_value("current_nozzle_id", new ConfigOptionInt(old_nozzle_id));
|
||||
dyn_config.set_key_value("next_nozzle_id", new ConfigOptionInt(next_nozzle_id));
|
||||
dyn_config.set_key_value("current_filament_id", new ConfigOptionInt(old_filament_id));
|
||||
@@ -9977,7 +9953,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
|
||||
this->placeholder_parser().set("current_extruder", new_filament_id);
|
||||
this->placeholder_parser().set("current_hotend",
|
||||
hotend_id_for_gcode_placeholder(m_config, group_result, (int) new_filament_id, new_extruder_id, m_layer_index));
|
||||
hotend_id_for_gcode_placeholder(group_result, m_print->is_BBL_printer(), (int) new_filament_id, new_extruder_id, m_layer_index));
|
||||
// Orca: keep the global current-tool identity coherent for later contexts (see append_tcr).
|
||||
this->placeholder_parser().set("current_filament_id", (int) new_filament_id);
|
||||
this->placeholder_parser().set("current_extruder_id", new_extruder_id);
|
||||
|
||||
@@ -148,8 +148,6 @@ struct ConflictComputeResult
|
||||
|
||||
using ConflictComputeOpt = std::optional<ConflictComputeResult>;
|
||||
|
||||
using ConflictObjName = std::optional<std::pair<std::string, std::string>>;
|
||||
|
||||
struct ConflictChecker
|
||||
{
|
||||
static ConflictResultOpt find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, std::optional<const FakeWipeTower *> wtdptr);
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <string>
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
namespace Slic3r {
|
||||
namespace PreciseSeam {
|
||||
|
||||
// Import EnforcedBlockedSeamPoint from SeamPlacerImpl namespace for convenience
|
||||
using SeamPlacerImpl::EnforcedBlockedSeamPoint;
|
||||
|
||||
// Geometry and its exterior bounds are prepared together, then treated as read-only.
|
||||
struct ModifierRegion {
|
||||
ExPolygon polygon;
|
||||
@@ -137,7 +134,7 @@ SegmentExtraction extract_perimeter_segments(const PreparedPerimeter &prepared,
|
||||
|
||||
// Result of weak modifier segment processing
|
||||
struct WeakModifierSegment {
|
||||
EnforcedBlockedSeamPoint type; // Enforced/Blocked/Neutral
|
||||
SeamPlacerImpl::EnforcedBlockedSeamPoint type; // Enforced/Blocked/Neutral
|
||||
Point left_point; // Coordinates of left (first) point of segment
|
||||
PerimeterPosition left_position; // Position on the source perimeter before insertion/refinement.
|
||||
Point right_point; // Coordinates of right (last) point of segment
|
||||
|
||||
@@ -40,8 +40,7 @@ std::string get_error_string(const ThumbnailErrors& errors);
|
||||
|
||||
|
||||
typedef std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> GCodeThumbnailDefinitionsList;
|
||||
using namespace std::literals;
|
||||
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG"sv);
|
||||
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG");
|
||||
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const ConfigBase &config);
|
||||
|
||||
|
||||
|
||||
@@ -1,580 +0,0 @@
|
||||
#include "GCodeSender.hpp"
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#if defined(__APPLE__) || defined(__OpenBSD__)
|
||||
#include <termios.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <sys/ioctl.h>
|
||||
#include <IOKit/serial/ioss.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include "/usr/include/asm-generic/ioctls.h"
|
||||
|
||||
/* The following definitions are kindly borrowed from:
|
||||
/usr/include/asm-generic/termbits.h
|
||||
Unfortunately we cannot just include that one because
|
||||
it would redefine the "struct termios" already defined
|
||||
the <termios.h> already included by Boost.ASIO. */
|
||||
#define K_NCCS 19
|
||||
struct termios2 {
|
||||
tcflag_t c_iflag;
|
||||
tcflag_t c_oflag;
|
||||
tcflag_t c_cflag;
|
||||
tcflag_t c_lflag;
|
||||
cc_t c_line;
|
||||
cc_t c_cc[K_NCCS];
|
||||
speed_t c_ispeed;
|
||||
speed_t c_ospeed;
|
||||
};
|
||||
#define BOTHER CBAUDEX
|
||||
|
||||
#endif
|
||||
|
||||
//#define DEBUG_SERIAL
|
||||
#ifdef DEBUG_SERIAL
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
std::fstream fs;
|
||||
#endif
|
||||
|
||||
#define KEEP_SENT 20
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
GCodeSender::GCodeSender()
|
||||
: io(), serial(io), can_send(false), sent(0), open(false), error(false),
|
||||
connected(false), queue_paused(false)
|
||||
{
|
||||
#ifdef DEBUG_SERIAL
|
||||
std::srand(std::time(nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
GCodeSender::~GCodeSender()
|
||||
{
|
||||
this->disconnect();
|
||||
}
|
||||
|
||||
bool
|
||||
GCodeSender::connect(std::string devname, unsigned int baud_rate)
|
||||
{
|
||||
this->disconnect();
|
||||
|
||||
this->set_error_status(false);
|
||||
try {
|
||||
this->serial.open(devname);
|
||||
|
||||
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd));
|
||||
this->serial.set_option(boost::asio::serial_port_base::character_size(boost::asio::serial_port_base::character_size(8)));
|
||||
this->serial.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
|
||||
this->serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
|
||||
this->set_baud_rate(baud_rate);
|
||||
|
||||
this->serial.close();
|
||||
this->serial.open(devname);
|
||||
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
|
||||
|
||||
// set baud rate again because set_option overwrote it
|
||||
this->set_baud_rate(baud_rate);
|
||||
this->open = true;
|
||||
this->reset();
|
||||
} catch (boost::system::system_error &) {
|
||||
this->set_error_status(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// a reset firmware expect line numbers to start again from 1
|
||||
this->sent = 0;
|
||||
this->last_sent.clear();
|
||||
|
||||
/* Initialize debugger */
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs.open("serial.txt", std::fstream::out | std::fstream::trunc);
|
||||
#endif
|
||||
|
||||
// this gives some work to the io_service before it is started
|
||||
// (post() runs the supplied function in its thread)
|
||||
this->io.post(boost::bind(&GCodeSender::do_read, this));
|
||||
|
||||
// start reading in the background thread
|
||||
boost::thread t(boost::bind(&boost::asio::io_service::run, &this->io));
|
||||
this->background_thread.swap(t);
|
||||
|
||||
// always send a M105 to check for connection because firmware might be silent on connect
|
||||
//FIXME Vojtech: This is being sent too early, leading to line number synchronization issues,
|
||||
// from which the GCodeSender never recovers.
|
||||
// this->send("M105", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::set_baud_rate(unsigned int baud_rate)
|
||||
{
|
||||
try {
|
||||
// This does not support speeds > 115200
|
||||
this->serial.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
|
||||
} catch (boost::system::system_error &) {
|
||||
boost::asio::serial_port::native_handle_type handle = this->serial.native_handle();
|
||||
|
||||
#if __APPLE__
|
||||
termios ios;
|
||||
::tcgetattr(handle, &ios);
|
||||
::cfsetspeed(&ios, baud_rate);
|
||||
speed_t newSpeed = baud_rate;
|
||||
ioctl(handle, IOSSIOSPEED, &newSpeed);
|
||||
::tcsetattr(handle, TCSANOW, &ios);
|
||||
#elif __linux__
|
||||
termios2 ios;
|
||||
if (ioctl(handle, TCGETS2, &ios))
|
||||
printf("Error in TCGETS2: %s\n", strerror(errno));
|
||||
ios.c_ispeed = ios.c_ospeed = baud_rate;
|
||||
ios.c_cflag &= ~CBAUD;
|
||||
ios.c_cflag |= BOTHER | CLOCAL | CREAD;
|
||||
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
|
||||
ios.c_cc[VTIME] = 1;
|
||||
if (ioctl(handle, TCSETS2, &ios))
|
||||
printf("Error in TCSETS2: %s\n", strerror(errno));
|
||||
|
||||
#elif __OpenBSD__
|
||||
struct termios ios;
|
||||
::tcgetattr(handle, &ios);
|
||||
::cfsetspeed(&ios, baud_rate);
|
||||
if (::tcsetattr(handle, TCSAFLUSH, &ios) != 0)
|
||||
printf("Failed to set baud rate: %s\n", strerror(errno));
|
||||
#else
|
||||
//throw Slic3r::InvalidArgument("OS does not currently support custom bauds");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::disconnect()
|
||||
{
|
||||
if (!this->open) return;
|
||||
this->open = false;
|
||||
this->connected = false;
|
||||
this->io.post(boost::bind(&GCodeSender::do_close, this));
|
||||
this->background_thread.join();
|
||||
this->io.reset();
|
||||
/*
|
||||
if (this->error_status()) {
|
||||
throw(boost::system::system_error(boost::system::error_code(),
|
||||
"Error while closing the device"));
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "DISCONNECTED" << std::endl << std::flush;
|
||||
fs.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
GCodeSender::is_connected() const
|
||||
{
|
||||
return this->connected;
|
||||
}
|
||||
|
||||
bool
|
||||
GCodeSender::wait_connected(unsigned int timeout) const
|
||||
{
|
||||
using namespace boost::posix_time;
|
||||
ptime t0 = second_clock::local_time() + seconds(timeout);
|
||||
while (!this->connected) {
|
||||
if (second_clock::local_time() > t0) return false;
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
GCodeSender::queue_size() const
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
return this->queue.size();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::pause_queue()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->queue_paused = true;
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::resume_queue()
|
||||
{
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->queue_paused = false;
|
||||
}
|
||||
this->send();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::purge_queue(bool priority)
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
if (priority) {
|
||||
// clear priority queue
|
||||
std::list<std::string> empty;
|
||||
std::swap(this->priqueue, empty);
|
||||
} else {
|
||||
// clear queue
|
||||
std::queue<std::string> empty;
|
||||
std::swap(this->queue, empty);
|
||||
this->queue_paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
// purge log and return its contents
|
||||
std::vector<std::string>
|
||||
GCodeSender::purge_log()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
std::vector<std::string> retval;
|
||||
retval.reserve(this->log.size());
|
||||
while (!this->log.empty()) {
|
||||
retval.push_back(this->log.front());
|
||||
this->log.pop();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCodeSender::getT() const
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
return this->T;
|
||||
}
|
||||
|
||||
std::string
|
||||
GCodeSender::getB() const
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
return this->B;
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::do_close()
|
||||
{
|
||||
this->set_error_status(false);
|
||||
boost::system::error_code ec;
|
||||
this->serial.cancel(ec);
|
||||
if (ec) this->set_error_status(true);
|
||||
this->serial.close(ec);
|
||||
if (ec) this->set_error_status(true);
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::set_error_status(bool e)
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->error_mutex);
|
||||
this->error = e;
|
||||
}
|
||||
|
||||
bool
|
||||
GCodeSender::error_status() const
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->error_mutex);
|
||||
return this->error;
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::do_read()
|
||||
{
|
||||
// read one line
|
||||
boost::asio::async_read_until(
|
||||
this->serial,
|
||||
this->read_buffer,
|
||||
'\n',
|
||||
boost::bind(
|
||||
&GCodeSender::on_read,
|
||||
this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::on_read(const boost::system::error_code& error,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
this->set_error_status(false);
|
||||
if (error) {
|
||||
#ifdef __APPLE__
|
||||
if (error.value() == 45) {
|
||||
// OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
|
||||
this->do_read();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// printf("ERROR: [%d] %s\n", error.value(), error.message().c_str());
|
||||
// error can be true even because the serial port was closed.
|
||||
// In this case it is not a real error, so ignore.
|
||||
if (this->open) {
|
||||
this->do_close();
|
||||
this->set_error_status(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::istream is(&this->read_buffer);
|
||||
std::string line;
|
||||
std::getline(is, line);
|
||||
if (!line.empty()) {
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "<< " << line << std::endl << std::flush;
|
||||
#endif
|
||||
|
||||
// note that line might contain \r at its end
|
||||
// parse incoming line
|
||||
if (!this->connected
|
||||
&& (boost::starts_with(line, "start")
|
||||
|| boost::starts_with(line, "Grbl ")
|
||||
|| boost::starts_with(line, "ok")
|
||||
|| boost::contains(line, "T:"))) {
|
||||
this->connected = true;
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->can_send = true;
|
||||
}
|
||||
this->send();
|
||||
} else if (boost::starts_with(line, "ok")) {
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
this->can_send = true;
|
||||
}
|
||||
this->send();
|
||||
} else if (boost::istarts_with(line, "resend") // Marlin uses "Resend: "
|
||||
|| boost::istarts_with(line, "rs")) {
|
||||
// extract the first number from line
|
||||
boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
|
||||
size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "!! line num out of sync: toresend = " << toresend << ", sent = " << sent << ", last_sent.size = " << last_sent.size() << std::endl;
|
||||
#endif
|
||||
|
||||
if (toresend > this->sent - this->last_sent.size() && toresend <= this->sent) {
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
|
||||
const auto lines_to_resend = this->sent - toresend + 1;
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << "!! resending " << lines_to_resend << " lines" << std::endl;
|
||||
#endif
|
||||
// move the unsent lines to priqueue
|
||||
this->priqueue.insert(
|
||||
this->priqueue.begin(), // insert at the beginning
|
||||
this->last_sent.begin() + this->last_sent.size() - lines_to_resend,
|
||||
this->last_sent.end()
|
||||
);
|
||||
|
||||
// we can empty last_sent because it's not useful anymore
|
||||
this->last_sent.clear();
|
||||
|
||||
// start resending with the requested line number
|
||||
this->sent = toresend - 1;
|
||||
this->can_send = true;
|
||||
}
|
||||
this->send();
|
||||
} else {
|
||||
printf("Cannot resend %zu (oldest we have is %zu)\n", toresend, this->sent - this->last_sent.size());
|
||||
}
|
||||
} else if (boost::starts_with(line, "wait")) {
|
||||
// ignore
|
||||
} else {
|
||||
// push any other line into the log
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
this->log.push(line);
|
||||
}
|
||||
|
||||
// parse temperature info
|
||||
{
|
||||
size_t pos = line.find("T:");
|
||||
if (pos != std::string::npos && line.size() > pos + 2) {
|
||||
// we got temperature info
|
||||
boost::lock_guard<boost::mutex> l(this->log_mutex);
|
||||
this->T = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2));
|
||||
|
||||
pos = line.find("B:");
|
||||
if (pos != std::string::npos && line.size() > pos + 2) {
|
||||
// we got bed temperature info
|
||||
this->B = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this->do_read();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::send(const std::vector<std::string> &lines, bool priority)
|
||||
{
|
||||
// append lines to queue
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
for (std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
|
||||
if (priority) {
|
||||
this->priqueue.push_back(*line);
|
||||
} else {
|
||||
this->queue.push(*line);
|
||||
}
|
||||
}
|
||||
}
|
||||
this->send();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::send(const std::string &line, bool priority)
|
||||
{
|
||||
// append line to queue
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
if (priority) {
|
||||
this->priqueue.push_back(line);
|
||||
} else {
|
||||
this->queue.push(line);
|
||||
}
|
||||
}
|
||||
this->send();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::send()
|
||||
{
|
||||
this->io.post(boost::bind(&GCodeSender::do_send, this));
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::do_send()
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(this->queue_mutex);
|
||||
|
||||
// printer is not connected or we're still waiting for the previous ack
|
||||
if (!this->can_send) return;
|
||||
|
||||
std::string line;
|
||||
while (!this->priqueue.empty() || (!this->queue.empty() && !this->queue_paused)) {
|
||||
if (!this->priqueue.empty()) {
|
||||
line = this->priqueue.front();
|
||||
this->priqueue.pop_front();
|
||||
} else {
|
||||
line = this->queue.front();
|
||||
this->queue.pop();
|
||||
}
|
||||
|
||||
// strip comments
|
||||
size_t comment_pos = line.find_first_of(';');
|
||||
if (comment_pos != std::string::npos)
|
||||
line.erase(comment_pos, std::string::npos);
|
||||
boost::algorithm::trim(line);
|
||||
|
||||
// if line is not empty, send it
|
||||
if (!line.empty()) break;
|
||||
// if line is empty, process next item in queue
|
||||
}
|
||||
if (line.empty()) return;
|
||||
|
||||
// compute full line
|
||||
++ this->sent;
|
||||
#ifndef DEBUG_SERIAL
|
||||
const auto line_num = this->sent;
|
||||
#else
|
||||
// In DEBUG_SERIAL mode, test line re-synchronization by sending bad line number 1/4 of the time
|
||||
const auto line_num = std::rand() < RAND_MAX/4 ? 0 : this->sent;
|
||||
#endif
|
||||
std::string full_line = "N" + boost::lexical_cast<std::string>(line_num) + " " + line;
|
||||
|
||||
// calculate checksum
|
||||
int cs = 0;
|
||||
for (std::string::const_iterator it = full_line.begin(); it != full_line.end(); ++it)
|
||||
cs = cs ^ *it;
|
||||
|
||||
// write line to device
|
||||
full_line += "*";
|
||||
full_line += boost::lexical_cast<std::string>(cs);
|
||||
full_line += "\n";
|
||||
|
||||
#ifdef DEBUG_SERIAL
|
||||
fs << ">> " << full_line << std::flush;
|
||||
#endif
|
||||
|
||||
this->last_sent.push_back(line);
|
||||
this->can_send = false;
|
||||
|
||||
while (this->last_sent.size() > KEEP_SENT) {
|
||||
this->last_sent.pop_front();
|
||||
}
|
||||
|
||||
// we can't supply boost::asio::buffer(full_line) to async_write() because full_line is on the
|
||||
// stack and the buffer would lose its underlying storage causing memory corruption
|
||||
std::ostream os(&this->write_buffer);
|
||||
os << full_line;
|
||||
boost::asio::async_write(this->serial, this->write_buffer, boost::bind(&GCodeSender::on_write, this, boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::on_write(const boost::system::error_code& error,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
this->set_error_status(false);
|
||||
if (error) {
|
||||
if (this->open) {
|
||||
this->do_close();
|
||||
this->set_error_status(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this->do_send();
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::set_DTR(bool on)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__SYMBIAN32__)
|
||||
boost::asio::serial_port_service::native_handle_type handle = this->serial.native_handle();
|
||||
if (on)
|
||||
EscapeCommFunction(handle, SETDTR);
|
||||
else
|
||||
EscapeCommFunction(handle, CLRDTR);
|
||||
#else
|
||||
int fd = this->serial.native_handle();
|
||||
int status;
|
||||
ioctl(fd, TIOCMGET, &status);
|
||||
if (on)
|
||||
status |= TIOCM_DTR;
|
||||
else
|
||||
status &= ~TIOCM_DTR;
|
||||
ioctl(fd, TIOCMSET, &status);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
GCodeSender::reset()
|
||||
{
|
||||
set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
set_DTR(true);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
set_DTR(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,81 +0,0 @@
|
||||
#ifndef slic3r_GCodeSender_hpp_
|
||||
#define slic3r_GCodeSender_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include <queue>
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <cstddef>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/serial_port.hpp>
|
||||
#include <boost/asio/streambuf.hpp>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/bind/bind.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace asio = boost::asio;
|
||||
|
||||
class GCodeSender : private boost::noncopyable {
|
||||
public:
|
||||
GCodeSender();
|
||||
~GCodeSender();
|
||||
bool connect(std::string devname, unsigned int baud_rate);
|
||||
void send(const std::vector<std::string> &lines, bool priority = false);
|
||||
void send(const std::string &s, bool priority = false);
|
||||
void disconnect();
|
||||
bool error_status() const;
|
||||
bool is_connected() const;
|
||||
bool wait_connected(unsigned int timeout = 3) const;
|
||||
size_t queue_size() const;
|
||||
void pause_queue();
|
||||
void resume_queue();
|
||||
void purge_queue(bool priority = false);
|
||||
std::vector<std::string> purge_log();
|
||||
std::string getT() const;
|
||||
std::string getB() const;
|
||||
void set_DTR(bool on);
|
||||
void reset();
|
||||
|
||||
private:
|
||||
asio::io_service io;
|
||||
asio::serial_port serial;
|
||||
boost::thread background_thread;
|
||||
boost::asio::streambuf read_buffer, write_buffer;
|
||||
bool open; // whether the serial socket is connected
|
||||
bool connected; // whether the printer is online
|
||||
bool error;
|
||||
mutable boost::mutex error_mutex;
|
||||
|
||||
// this mutex guards queue, priqueue, can_send, queue_paused, sent, last_sent
|
||||
mutable boost::mutex queue_mutex;
|
||||
std::queue<std::string> queue;
|
||||
std::list<std::string> priqueue;
|
||||
bool can_send;
|
||||
bool queue_paused;
|
||||
size_t sent;
|
||||
std::deque<std::string> last_sent;
|
||||
|
||||
// this mutex guards log, T, B
|
||||
mutable boost::mutex log_mutex;
|
||||
std::queue<std::string> log;
|
||||
std::string T, B;
|
||||
|
||||
void set_baud_rate(unsigned int baud_rate);
|
||||
void set_error_status(bool e);
|
||||
void do_send();
|
||||
void on_write(const boost::system::error_code& error, size_t bytes_transferred);
|
||||
void do_close();
|
||||
void do_read();
|
||||
void on_read(const boost::system::error_code& error, size_t bytes_transferred);
|
||||
void send();
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GCodeSender_hpp_ */
|
||||
@@ -94,8 +94,6 @@ struct Circle {
|
||||
|
||||
using Circlef = Circle<Vec2f>;
|
||||
using Circled = Circle<Vec2d>;
|
||||
using CircleSqf = CircleSq<Vec2f>;
|
||||
using CircleSqd = CircleSq<Vec2d>;
|
||||
|
||||
/// Find the center of the circle corresponding to the vector of Points as an arc.
|
||||
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "MedialAxis.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
@@ -33,7 +33,7 @@ class PrintObject;
|
||||
class Print;
|
||||
|
||||
namespace FillAdaptive {
|
||||
struct Octree;
|
||||
struct Octrees;
|
||||
};
|
||||
|
||||
namespace FillLightning {
|
||||
@@ -170,10 +170,10 @@ public:
|
||||
ExPolygons lslices;
|
||||
ExPolygons lslices_extrudable; // BBS: the extrudable part of lslices used for tree support
|
||||
std::vector<BoundingBox> lslices_bboxes;
|
||||
// Orca: for separated infills / per-model centering. Aligned with lslices: for each island, the
|
||||
// full bounding box of the 3D connected body (across all layers) it belongs to. Populated by
|
||||
// PrintObject::infill() only when the feature is used; empty otherwise.
|
||||
std::vector<BoundingBox> lslices_separated_component_bboxes;
|
||||
// Orca: for separated infills / per-model centering / octree infills. Aligned with lslices: for each
|
||||
// island, the 3D connected body (across all layers) it belongs to, indexing
|
||||
// PrintObject::separated_body_bboxes(). Populated by PrintObject::prepare_infill() only when needed.
|
||||
std::vector<size_t> lslices_separated_component_ids;
|
||||
|
||||
// BBS
|
||||
ExPolygons loverhangs;
|
||||
@@ -208,9 +208,9 @@ public:
|
||||
void make_perimeters();
|
||||
// Phony version of make_fills() without parameters for Perl integration only.
|
||||
void make_fills() { this->make_fills(nullptr, nullptr); }
|
||||
void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator = nullptr);
|
||||
Polylines generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree *adaptive_fill_octree,
|
||||
FillAdaptive::Octree *support_fill_octree,
|
||||
void make_fills(const FillAdaptive::Octrees* adaptive_fill_octrees, const FillAdaptive::Octrees* support_fill_octrees, FillLightning::Generator* lightning_generator = nullptr);
|
||||
Polylines generate_sparse_infill_polylines_for_anchoring(const FillAdaptive::Octrees *adaptive_fill_octrees,
|
||||
const FillAdaptive::Octrees *support_fill_octrees,
|
||||
FillLightning::Generator* lightning_generator) const;
|
||||
void make_ironing();
|
||||
// Returns the filament id (1-based) the region is ironed with, or -1 when the
|
||||
|
||||
@@ -62,7 +62,7 @@ Flow LayerRegion::bridging_flow(FlowRole role, bool thick_bridge) const
|
||||
const PrintObject &print_object = *this->layer()->object();
|
||||
Flow bridge_flow;
|
||||
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will fall back to zero'th element, so everything is all right.
|
||||
auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
|
||||
auto nozzle_diameter = float(nozzle_diameter_for_filament(print_object.print()->config(), region.extruder(role), print_object.print()->is_BBL_printer()));
|
||||
const ConfigOptionFloatOrPercent& bridge_width_opt = region_config.bridge_line_width;
|
||||
const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter);
|
||||
const bool has_bridge_width = bridge_width > 0.;
|
||||
|
||||
@@ -154,13 +154,12 @@ template<class ExecutionPolicy, class Enable = void> struct _Loop
|
||||
};
|
||||
|
||||
// Add Specialization for using ExecutionTBB for parallel loops.
|
||||
using namespace Slic3r;
|
||||
template<> struct _Loop<ExecutionTBB>
|
||||
template<> struct _Loop<Slic3r::ExecutionTBB>
|
||||
{
|
||||
template<class It, class Fn> static void for_each_idx(It from, It to, Fn&& fn)
|
||||
{
|
||||
execution::for_each(
|
||||
ex_tbb, size_t(0), size_t(to - from), [&from, &fn](size_t i) { fn(from[i], i); }, execution::max_concurrency(ex_tbb));
|
||||
Slic3r::execution::for_each(
|
||||
Slic3r::ex_tbb, size_t(0), size_t(to - from), [&from, &fn](size_t i) { fn(from[i], i); }, Slic3r::execution::max_concurrency(Slic3r::ex_tbb));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "Format/AssimpImport.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "ModelArrange.hpp"
|
||||
#include "Arrange.hpp"
|
||||
#include "Geometry.hpp"
|
||||
@@ -1697,6 +1696,9 @@ indexed_triangle_set ModelObject::raw_indexed_triangle_set() const
|
||||
size_t j = out.indices.size();
|
||||
append(out.vertices, v->mesh().its.vertices);
|
||||
append(out.indices, v->mesh().its.indices);
|
||||
// Orca: Point the volume's triangles at its own vertices, which follow those of the volumes before it.
|
||||
for (size_t k = j; k < out.indices.size(); ++ k)
|
||||
out.indices[k] += stl_triangle_vertex_indices::Constant(int(i));
|
||||
const Transform3d& m = v->get_matrix();
|
||||
for (; i < out.vertices.size(); ++ i)
|
||||
out.vertices[i] = (m * out.vertices[i].cast<double>()).cast<float>().eval();
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include "calib.hpp"
|
||||
#include "enum_bitmask.hpp"
|
||||
#include "TextConfiguration.hpp"
|
||||
#include "EmbossShape.hpp"
|
||||
#include "TriangleSelector.hpp"
|
||||
|
||||
@@ -3593,24 +3593,6 @@ void PresetBundle::export_selections(AppConfig &config)
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": printer %1%, print %2%, filaments[0] %3% ")%printers.get_selected_preset_name() % prints.get_selected_preset_name() %filament_presets[0];
|
||||
}
|
||||
|
||||
// Preserve metadata only for existing colour slots; new slots get false/empty defaults.
|
||||
static void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count)
|
||||
{
|
||||
auto resize = [old_slot_count, new_slot_count](auto *opt) {
|
||||
if (opt) {
|
||||
opt->values.resize(std::min(old_slot_count, opt->values.size()));
|
||||
opt->values.resize(new_slot_count);
|
||||
}
|
||||
};
|
||||
resize(config.option<ConfigOptionBools>("filament_is_mixed"));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_components"));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios"));
|
||||
resize(config.option<ConfigOptionBools>("filament_mixed_gradient"));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_range"));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_curve"));
|
||||
resize(config.option<ConfigOptionBools>("filament_mixed_gradient_per_part"));
|
||||
}
|
||||
|
||||
void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
|
||||
{
|
||||
unsigned old_filament_count = this->filament_presets.size();
|
||||
@@ -3894,16 +3876,6 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
maps.erase(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_placeholder) {
|
||||
// Orca: an empty AMS slot is a placeholder, not a filament - never synthesize a
|
||||
// preset for it. Kept here only to keep index alignment with ams_infos; the sync
|
||||
// drops it so no filament slot is created or retained for an empty tray.
|
||||
ams_filament_presets.push_back("");
|
||||
ams_filament_colors.push_back("");
|
||||
ams_filament_color_types.push_back("");
|
||||
ams_multi_color_filment.push_back({});
|
||||
} else if (use_map) {
|
||||
ams_filament_presets.push_back("Generic PLA");//for unknow matieral
|
||||
auto default_unknown_color = "#CECECE";
|
||||
ams_filament_colors.push_back(default_unknown_color);
|
||||
@@ -3912,6 +3884,12 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
filament_multi_color.push_back(default_unknown_color);
|
||||
}
|
||||
ams_multi_color_filment.push_back(filament_multi_color);
|
||||
} else if (is_placeholder) {
|
||||
// Orca: push placeholders to keep index alignment with ams_infos
|
||||
ams_filament_presets.push_back("");
|
||||
ams_filament_colors.push_back("");
|
||||
ams_filament_color_types.push_back("");
|
||||
ams_multi_color_filment.push_back({});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -4275,10 +4253,6 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
result_multi_colors.push_back(
|
||||
i < ams_multi_color_filment.size() ? ams_multi_color_filment[i]
|
||||
: std::vector<std::string>{ams_filament_colors[i]});
|
||||
} else if (i < ams_infos.size() && ams_infos[i].is_placeholder) {
|
||||
// Orca: an empty AMS slot is a placeholder, not a filament, so drop it instead of
|
||||
// keeping or filling a slot for it. The device AMS panel still shows the empty tray.
|
||||
continue;
|
||||
} else if (i < exist_presets.size()) {
|
||||
// Empty tray or beyond tray count: keep existing filament
|
||||
result_colors.push_back(exist_colors[i]);
|
||||
@@ -4303,8 +4277,8 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
||||
filament_color_type->values = result_color_types;
|
||||
this->filament_presets = result_presets;
|
||||
ams_multi_color_filment = result_multi_colors;
|
||||
filament_map->values.resize(result_presets.size(), 1);
|
||||
filament_volume_map->values.resize(result_presets.size(), static_cast<int>(NozzleVolumeType::nvtStandard));
|
||||
filament_map->values.resize(total, 1);
|
||||
filament_volume_map->values.resize(total, static_cast<int>(NozzleVolumeType::nvtStandard));
|
||||
} else {
|
||||
// BBL: existing wholesale replace
|
||||
filament_color->values = ams_filament_colors;
|
||||
|
||||
@@ -2229,7 +2229,7 @@ StringObjectException Print::validate(std::vector<StringObjectException> *warnin
|
||||
for (const PrintRegion ®ion : object->all_regions()) {
|
||||
const auto &bridge_width_opt = region.config().bridge_line_width;
|
||||
for (FlowRole bridge_role : { frPerimeter, frInfill, frSolidInfill, frTopSolidInfill }) {
|
||||
const double nozzle_diameter = m_config.nozzle_diameter.get_at(region.extruder(bridge_role) - 1);
|
||||
const double nozzle_diameter = nozzle_diameter_for_filament(m_config, region.extruder(bridge_role), this->is_BBL_printer());
|
||||
const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter);
|
||||
if (bridge_width <= 0.)
|
||||
continue;
|
||||
@@ -2606,7 +2606,7 @@ Flow Print::brim_flow() const
|
||||
frPerimeter,
|
||||
// Flow::new_from_config_width takes care of the percent to value substitution
|
||||
width,
|
||||
(float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().outer_wall_filament_id-1),
|
||||
(float)nozzle_diameter_for_filament(m_config, m_print_regions.front()->config().outer_wall_filament_id, this->is_BBL_printer()),
|
||||
(float)this->skirt_first_layer_height());
|
||||
}
|
||||
|
||||
@@ -2623,12 +2623,13 @@ Flow Print::skirt_flow() const
|
||||
extruders and take the one with, say, the smallest index;
|
||||
The same logic should be applied to the code that selects the extruder during G-code
|
||||
generation as well. */
|
||||
return Flow::new_from_config_width(frPerimeter,
|
||||
// Flow::new_from_config_width takes care of the percent to value substitution
|
||||
width,
|
||||
(float) m_config.nozzle_diameter.get_at(
|
||||
m_objects.empty() ? 0 : m_objects.front()->config().support_filament - 1),
|
||||
(float) this->skirt_first_layer_height());
|
||||
return Flow::new_from_config_width(
|
||||
frPerimeter,
|
||||
// Flow::new_from_config_width takes care of the percent to value substitution
|
||||
width,
|
||||
// ORCA: resolve the actual nozzle the support filament is printed with (dual-nozzle printers).
|
||||
(float)nozzle_diameter_for_filament(m_config, m_objects.empty() ? 0 : m_objects.front()->config().support_filament, this->is_BBL_printer()),
|
||||
(float)this->skirt_first_layer_height());
|
||||
}
|
||||
|
||||
bool Print::has_support_material() const
|
||||
|
||||
@@ -375,6 +375,8 @@ public:
|
||||
Transform3d trafo_centered() const
|
||||
{ Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(m_center_offset.y()), 0)); return t; }
|
||||
const PrintInstances& instances() const { return m_instances; }
|
||||
// Orca: Bounding box of each connected body, indexed by Layer::lslices_separated_component_ids.
|
||||
const std::vector<BoundingBox>& separated_body_bboxes() const { return m_separated_body_bboxes; }
|
||||
PrintInstances &instances() { return m_instances; }
|
||||
|
||||
// Whoever will get a non-const pointer to PrintObject will be able to modify its layers.
|
||||
@@ -581,8 +583,8 @@ private:
|
||||
void discover_horizontal_shells();
|
||||
void combine_infill();
|
||||
void _generate_support_material();
|
||||
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data(
|
||||
const std::vector<std::pair<const Surface*, float>>& surfaces_w_bottom_z) const;
|
||||
std::pair<FillAdaptive::Octrees, FillAdaptive::Octrees> prepare_adaptive_infill_data(
|
||||
const std::vector<std::pair<const Surface*, const Layer*>>& surfaces_w_layer) const;
|
||||
FillLightning::GeneratorPtr prepare_lightning_infill_data();
|
||||
|
||||
// BBS
|
||||
@@ -614,7 +616,8 @@ private:
|
||||
// so that next call to make_perimeters() performs a union() before computing loops
|
||||
bool m_typed_slices = false;
|
||||
|
||||
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> m_adaptive_fill_octrees;
|
||||
std::pair<FillAdaptive::Octrees, FillAdaptive::Octrees> m_adaptive_fill_octrees;
|
||||
std::vector<BoundingBox> m_separated_body_bboxes;
|
||||
FillLightning::GeneratorPtr m_lightning_generator;
|
||||
|
||||
std::vector < VolumeSlices > firstLayerObjSliceByVolume;
|
||||
|
||||
@@ -133,6 +133,15 @@ size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
double nozzle_diameter_for_filament(const PrintConfig& config, int filament_id, bool is_bbl_printer)
|
||||
{
|
||||
int extruder = filament_id;
|
||||
if (is_bbl_printer && config.nozzle_diameter.size() > 1 &&
|
||||
filament_id >= 1 && static_cast<size_t>(filament_id - 1) < config.filament_map.size())
|
||||
extruder = config.filament_map.get_at(filament_id - 1);
|
||||
return config.nozzle_diameter.get_at(extruder - 1);
|
||||
}
|
||||
|
||||
|
||||
// Orca: input shaping values types by flavor
|
||||
std::vector<std::string> get_shaper_type_values_for_flavor(GCodeFlavor flavor)
|
||||
@@ -7602,8 +7611,8 @@ void PrintConfigDef::init_fff_params()
|
||||
"whole assembly. Parts that touch or overlap are treated as one body and share a center; separate parts "
|
||||
"(or distinct 3D objects) each get their own.\n"
|
||||
"Useful when an assembly groups several objects that should each keep a consistent, self-centered infill.\n"
|
||||
"Affects line and grid patterns and rotation-template infills.\n"
|
||||
"Patterns locked to global coordinates (Gyroid, Honeycomb, TPMS, ...) are unaffected.");
|
||||
"Adaptive Cubic and Support Cubic always center each part on itself, and Lightning infill is generated for "
|
||||
"the whole object and is unaffected.");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
@@ -10845,6 +10854,21 @@ void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<cons
|
||||
}
|
||||
}
|
||||
|
||||
void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count)
|
||||
{
|
||||
auto resize = [old_slot_count, new_slot_count](auto *opt) {
|
||||
opt->values.resize(std::min(old_slot_count, opt->values.size()));
|
||||
opt->values.resize(new_slot_count);
|
||||
};
|
||||
resize(config.option<ConfigOptionBools>("filament_is_mixed", true));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_components", true));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios", true));
|
||||
resize(config.option<ConfigOptionBools>("filament_mixed_gradient", true));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_range", true));
|
||||
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_curve", true));
|
||||
resize(config.option<ConfigOptionBools>("filament_mixed_gradient_per_part", true));
|
||||
}
|
||||
|
||||
|
||||
//used for object/region config
|
||||
//use the smallest of multiple to single
|
||||
|
||||
@@ -136,25 +136,31 @@ enum InfillPattern : int {
|
||||
ipCount,
|
||||
};
|
||||
|
||||
// Orca: Infill patterns whose alignment origin follows the fill bounding box, so the
|
||||
// "separated_infills" option can re-center them per connected body. Patterns evaluated in
|
||||
// absolute/global coordinates (Gyroid, TPMS, Honeycomb, CrossHatch, ...) or that are shape-relative
|
||||
// (Concentric) ignore that bounding box and are therefore excluded.
|
||||
// Orca: Infill patterns that the "separated_infills" option can center on each connected body.
|
||||
inline bool is_separable_infill_pattern(InfillPattern pattern)
|
||||
{
|
||||
switch (pattern) {
|
||||
case ipMonotonic:
|
||||
case ipMonotonicLine:
|
||||
case ipRectilinear:
|
||||
case ipAlignedRectilinear:
|
||||
case ipZigZag:
|
||||
case ipCrossZag:
|
||||
case ipLockedZag:
|
||||
case ipLine:
|
||||
case ipGrid:
|
||||
case ipTriangles:
|
||||
case ipStars: // tri-hexagon
|
||||
case ipCubic:
|
||||
case ipQuarterCubic:
|
||||
case ipHoneycomb:
|
||||
case ip3DHoneycomb:
|
||||
case ipLateralHoneycomb:
|
||||
case ipLateralLattice:
|
||||
case ipCrossHatch:
|
||||
case ipTpmsD:
|
||||
case ipTpmsFK:
|
||||
case ipGyroid:
|
||||
case ipHilbertCurve:
|
||||
case ipArchimedeanChords:
|
||||
case ipOctagramSpiral:
|
||||
@@ -164,6 +170,9 @@ inline bool is_separable_infill_pattern(InfillPattern pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: Infill patterns laid out by an octree, which each connected body always gets of its own.
|
||||
inline bool is_octree_infill_pattern(InfillPattern pattern) { return pattern == ipAdaptiveCubic || pattern == ipSupportCubic; }
|
||||
|
||||
// Orca: Infill patterns that round their corners by the "sparse_infill_smooth_factor" option.
|
||||
// Grid, Triangles and Tri-hexagon only do so in their trapezoidal form, which is generated with more
|
||||
// than one line per infill wall; a single line makes them plain crossing lines with nothing to round.
|
||||
@@ -933,6 +942,10 @@ extern std::set<std::string> filament_dev_options;
|
||||
// filament_configs, one config per filament in slot order, as the filaments' values one after another.
|
||||
void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<const DynamicPrintConfig *> &filament_configs);
|
||||
|
||||
// Orca: sizes the per-slot mixed-colour metadata options to new_slot_count, keeping the first
|
||||
// old_slot_count values; an option the config lacks is created.
|
||||
void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count);
|
||||
|
||||
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
|
||||
extern void compute_filament_override_value(const std::string& opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig& new_full_config,
|
||||
t_config_option_keys& diff_keys, DynamicPrintConfig& filament_overrides, std::vector<int>& f_map_indices);
|
||||
@@ -2562,6 +2575,8 @@ static bool has_zero_flush_volume_for_used_filaments(const std::vector<T> &fv_ma
|
||||
|
||||
size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id);
|
||||
|
||||
double nozzle_diameter_for_filament(const PrintConfig& config, int filament_id, bool is_bbl_printer);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
// Serialization through the Cereal library
|
||||
|
||||
+125
-34
@@ -67,6 +67,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <Eigen/Core>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
@@ -719,7 +720,8 @@ void PrintObject::prepare_infill()
|
||||
bool needs_separated_components = false;
|
||||
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
|
||||
const PrintRegionConfig &rc = this->printing_region(i).config();
|
||||
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) {
|
||||
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model ||
|
||||
(rc.sparse_infill_density > 0 && is_octree_infill_pattern(rc.sparse_infill_pattern))) {
|
||||
needs_separated_components = true;
|
||||
break;
|
||||
}
|
||||
@@ -736,8 +738,9 @@ void PrintObject::prepare_infill()
|
||||
if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
|
||||
needs_separated_components = false;
|
||||
}
|
||||
m_separated_body_bboxes.clear();
|
||||
for (Layer *layer : m_layers)
|
||||
layer->lslices_separated_component_bboxes.clear();
|
||||
layer->lslices_separated_component_ids.clear();
|
||||
if (needs_separated_components) {
|
||||
const size_t nl = m_layers.size();
|
||||
std::vector<size_t> offset(nl + 1, 0); // Orca: flat index of the first island of each layer
|
||||
@@ -788,17 +791,20 @@ void PrintObject::prepare_infill()
|
||||
});
|
||||
}
|
||||
}
|
||||
// Orca: Full bounding box of each body, indexed by its union-find root.
|
||||
std::vector<BoundingBox> body_bbox(nreg);
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a)
|
||||
body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]);
|
||||
// Orca: Store the body bbox for every island.
|
||||
// Orca: Number the bodies by their first island and merge the bounding boxes of their islands.
|
||||
std::vector<size_t> body_of_root(nreg, size_t(-1));
|
||||
for (size_t i = 0; i < nl; ++ i) {
|
||||
Layer *layer = m_layers[i];
|
||||
layer->lslices_separated_component_bboxes.resize(layer->lslices.size());
|
||||
for (size_t a = 0; a < layer->lslices.size(); ++ a)
|
||||
layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)];
|
||||
layer->lslices_separated_component_ids.resize(layer->lslices.size());
|
||||
for (size_t a = 0; a < layer->lslices.size(); ++ a) {
|
||||
size_t &body = body_of_root[find(offset[i] + a)];
|
||||
if (body == size_t(-1)) {
|
||||
body = m_separated_body_bboxes.size();
|
||||
m_separated_body_bboxes.emplace_back();
|
||||
}
|
||||
m_separated_body_bboxes[body].merge(layer->lslices_bboxes[a]);
|
||||
layer->lslices_separated_component_ids[a] = body;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,16 +842,13 @@ void PrintObject::infill()
|
||||
if (this->set_started(posInfill)) {
|
||||
m_print->set_status(35, L("Generating infill toolpath"));
|
||||
|
||||
const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first;
|
||||
const auto& support_fill_octree = this->m_adaptive_fill_octrees.second;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) {
|
||||
[this](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get());
|
||||
m_layers[layer_idx]->make_fills(&m_adaptive_fill_octrees.first, &m_adaptive_fill_octrees.second, this->m_lightning_generator.get());
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1110,14 +1113,69 @@ void PrintObject::simplify_extrusion_path()
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare_adaptive_infill_data(
|
||||
const std::vector<std::pair<const Surface *, float>> &surfaces_w_bottom_z) const
|
||||
// Orca: Separated body of the island containing a point of a layer, else of the island outline nearest within 1 mm, or -1.
|
||||
static int separated_body_at(const Layer &layer, const Point &point)
|
||||
{
|
||||
int body = -1;
|
||||
double best = scaled<double>(1.);
|
||||
for (size_t i = 0; i < layer.lslices.size() && i < layer.lslices_separated_component_ids.size() && best > 0.; ++ i) {
|
||||
BoundingBox bbox = layer.lslices_bboxes[i];
|
||||
bbox.offset(coord_t(best));
|
||||
if (! bbox.contains(point))
|
||||
continue;
|
||||
const double dist = layer.lslices[i].contains(point) ? 0. : (layer.lslices[i].point_projection(point) - point).cast<double>().norm();
|
||||
if (dist < best) {
|
||||
best = dist;
|
||||
body = int(layer.lslices_separated_component_ids[i]);
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
// Orca: The object mesh in the octree frame split by separated body. Each connected component goes to the body
|
||||
// most of its sampled triangles lie on, sampled a layer height inside the solid at the layer nearest to them.
|
||||
static std::vector<indexed_triangle_set> split_mesh_by_body(const PrintObject &object, const indexed_triangle_set &mesh, size_t num_bodies)
|
||||
{
|
||||
const Eigen::Matrix3d to_object = FillAdaptive::transform_to_world().toRotationMatrix();
|
||||
const double inset = object.config().layer_height.value;
|
||||
std::vector<indexed_triangle_set> bodies(num_bodies);
|
||||
for (const indexed_triangle_set &component : its_split(mesh)) {
|
||||
std::vector<size_t> votes(num_bodies, 0);
|
||||
const size_t step = std::max<size_t>(1, component.indices.size() / 8);
|
||||
for (size_t i = 0; i < component.indices.size(); i += step) {
|
||||
const stl_triangle_vertex_indices &tri = component.indices[i];
|
||||
const Vec3d a = component.vertices[tri[0]].cast<double>(), b = component.vertices[tri[1]].cast<double>(),
|
||||
d = component.vertices[tri[2]].cast<double>();
|
||||
const Vec3d normal = (b - a).cross(d - a);
|
||||
const double area2 = normal.norm();
|
||||
const Vec3d c = to_object * ((a + b + d) / 3. - (area2 > 0. ? Vec3d(normal * (inset / area2)) : Vec3d::Zero()));
|
||||
size_t lo = 0, hi = object.layer_count();
|
||||
while (lo < hi) {
|
||||
const size_t mid = (lo + hi) / 2;
|
||||
if (object.get_layer(int(mid))->slice_z < c.z())
|
||||
lo = mid + 1;
|
||||
else
|
||||
hi = mid;
|
||||
}
|
||||
if (lo == object.layer_count() || (lo > 0 && c.z() - object.get_layer(int(lo) - 1)->slice_z < object.get_layer(int(lo))->slice_z - c.z()))
|
||||
-- lo;
|
||||
if (const int body = separated_body_at(*object.get_layer(int(lo)), Point(scaled<coord_t>(c.x()), scaled<coord_t>(c.y()))); body >= 0)
|
||||
++ votes[body];
|
||||
}
|
||||
if (const auto best = std::max_element(votes.begin(), votes.end()); *best > 0)
|
||||
its_merge(bodies[best - votes.begin()], component);
|
||||
}
|
||||
return bodies;
|
||||
}
|
||||
|
||||
std::pair<FillAdaptive::Octrees, FillAdaptive::Octrees> PrintObject::prepare_adaptive_infill_data(
|
||||
const std::vector<std::pair<const Surface *, const Layer *>> &surfaces_w_layer) const
|
||||
{
|
||||
using namespace FillAdaptive;
|
||||
|
||||
auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this);
|
||||
if ((adaptive_line_spacing == 0. && support_line_spacing == 0.) || this->layers().empty())
|
||||
return std::make_pair(OctreePtr(), OctreePtr());
|
||||
return {};
|
||||
|
||||
indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set();
|
||||
// Rotate mesh and build octree on it with axis-aligned (standart base) cubes.
|
||||
@@ -1125,27 +1183,60 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
|
||||
its_transform(mesh, to_octree * this->trafo_centered(), true);
|
||||
|
||||
// Triangulate internal bridging surfaces.
|
||||
std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1)));
|
||||
std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_layer.size(), size_t(1)));
|
||||
// ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_bottom_z.size()),
|
||||
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_layer.size()),
|
||||
[this, &to_octree, &overhangs, &surfaces_w_layer](const tbb::blocked_range<int> &range) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
|
||||
std::vector<Vec3d> &out = overhangs[surface_idx];
|
||||
m_print->throw_if_canceled();
|
||||
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
|
||||
surfaces_w_bottom_z[surface_idx].second));
|
||||
append(out, triangulate_expolygon_3d(surfaces_w_layer[surface_idx].first->expolygon,
|
||||
float(surfaces_w_layer[surface_idx].second->bottom_z())));
|
||||
for (Vec3d &p : out)
|
||||
p = (to_octree * p).eval();
|
||||
}
|
||||
});
|
||||
|
||||
// Orca: Each body gets the octree it has when sliced on its own, from its own triangles.
|
||||
std::pair<Octrees, Octrees> octrees;
|
||||
const size_t num_bodies = m_separated_body_bboxes.size();
|
||||
bool need_object = num_bodies <= 1;
|
||||
if (num_bodies > 1) {
|
||||
const std::vector<indexed_triangle_set> body_meshes = split_mesh_by_body(*this, mesh, num_bodies);
|
||||
need_object = std::any_of(body_meshes.begin(), body_meshes.end(), [](const indexed_triangle_set &its) { return its.indices.empty(); });
|
||||
std::vector<std::vector<Vec3d>> body_overhangs(num_bodies);
|
||||
for (size_t i = 0; i < surfaces_w_layer.size(); ++ i)
|
||||
if (const int body = separated_body_at(*surfaces_w_layer[i].second, surfaces_w_layer[i].first->expolygon.contour.points.front()); body >= 0)
|
||||
append(body_overhangs[body], overhangs[i]);
|
||||
if (adaptive_line_spacing)
|
||||
octrees.first.bodies.resize(num_bodies);
|
||||
if (support_line_spacing)
|
||||
octrees.second.bodies.resize(num_bodies);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_bodies), [&, adaptive_spacing = adaptive_line_spacing, support_spacing = support_line_spacing](
|
||||
const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t body = range.begin(); body < range.end(); ++ body) {
|
||||
m_print->throw_if_canceled();
|
||||
if (body_meshes[body].indices.empty())
|
||||
continue;
|
||||
if (adaptive_spacing)
|
||||
octrees.first.bodies[body] = build_octree(body_meshes[body], body_overhangs[body], adaptive_spacing, false);
|
||||
if (support_spacing)
|
||||
octrees.second.bodies[body] = build_octree(body_meshes[body], body_overhangs[body], support_spacing, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// and gather them.
|
||||
for (size_t i = 1; i < overhangs.size(); ++ i)
|
||||
append(overhangs.front(), std::move(overhangs[i]));
|
||||
|
||||
return std::make_pair(
|
||||
adaptive_line_spacing ? build_octree(mesh, overhangs.front(), adaptive_line_spacing, false) : OctreePtr(),
|
||||
support_line_spacing ? build_octree(mesh, overhangs.front(), support_line_spacing, true) : OctreePtr());
|
||||
// Orca: The object's octree only serves bodies that have none of their own.
|
||||
if (need_object && adaptive_line_spacing)
|
||||
octrees.first.object = build_octree(mesh, overhangs.front(), adaptive_line_spacing, false);
|
||||
if (need_object && support_line_spacing)
|
||||
octrees.second.object = build_octree(mesh, overhangs.front(), support_line_spacing, true);
|
||||
return octrees;
|
||||
}
|
||||
|
||||
FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data()
|
||||
@@ -2963,14 +3054,14 @@ void PrintObject::bridge_over_infill()
|
||||
std::map<size_t, Polylines> infill_lines;
|
||||
// SECTION to generate infill polylines
|
||||
{
|
||||
std::vector<std::pair<const Surface *, float>> surfaces_w_bottom_z;
|
||||
std::vector<std::pair<const Surface *, const Layer *>> surfaces_w_layer;
|
||||
for (const auto &pair : surfaces_by_layer) {
|
||||
for (const CandidateSurface &c : pair.second) {
|
||||
surfaces_w_bottom_z.emplace_back(c.original_surface, c.region->m_layer->bottom_z());
|
||||
surfaces_w_layer.emplace_back(c.original_surface, c.region->m_layer);
|
||||
}
|
||||
}
|
||||
|
||||
this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z);
|
||||
this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_layer);
|
||||
|
||||
std::vector<size_t> layers_to_generate_infill;
|
||||
for (const auto &pair : surfaces_by_layer) {
|
||||
@@ -2986,8 +3077,8 @@ void PrintObject::bridge_over_infill()
|
||||
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
|
||||
size_t lidx = layers_to_generate_infill[job_idx];
|
||||
infill_lines.at(
|
||||
lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->m_adaptive_fill_octrees.first.get(),
|
||||
po->m_adaptive_fill_octrees.second.get(),
|
||||
lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(&po->m_adaptive_fill_octrees.first,
|
||||
&po->m_adaptive_fill_octrees.second,
|
||||
po->m_lightning_generator.get());
|
||||
}
|
||||
});
|
||||
@@ -4467,8 +4558,8 @@ void PrintObject::combine_infill()
|
||||
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
|
||||
//FIXME limit the layer height to max_layer_height
|
||||
double nozzle_diameter = std::min(
|
||||
this->print()->config().nozzle_diameter.get_at(region.config().sparse_infill_filament_id.value - 1),
|
||||
this->print()->config().nozzle_diameter.get_at(region.config().internal_solid_filament_id.value - 1));
|
||||
nozzle_diameter_for_filament(this->print()->config(), region.config().sparse_infill_filament_id.value, this->print()->is_BBL_printer()),
|
||||
nozzle_diameter_for_filament(this->print()->config(), region.config().internal_solid_filament_id.value, this->print()->is_BBL_printer()));
|
||||
|
||||
//Orca: Limit combination of infill to up to infill_combination_max_layer_height
|
||||
const double infill_combination_max_layer_height = region.config().infill_combination_max_layer_height.get_abs_value(nozzle_diameter);
|
||||
|
||||
@@ -57,7 +57,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he
|
||||
|
||||
// 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.
|
||||
auto nozzle_diameter = float(print_config.nozzle_diameter.get_at(this->extruder(role) - 1));
|
||||
auto nozzle_diameter = float(nozzle_diameter_for_filament(print_config, this->extruder(role), object.print()->is_BBL_printer()));
|
||||
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height));
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ public:
|
||||
using TColor = typename PixelRenderer::color_type;
|
||||
using TValue = typename TColor::value_type;
|
||||
using TPixel = typename PixelRenderer::pixel_type;
|
||||
using TRawBuffer = agg::rendering_buffer;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
#include "TDataStd_Name.hxx"
|
||||
#include "BRepBuilderAPI_Transform.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "BRep_Tool.hxx"
|
||||
#include "Font_BRepFont.hxx"
|
||||
#include "Font_BRepTextBuilder.hxx"
|
||||
|
||||
@@ -2180,7 +2180,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers(
|
||||
|
||||
// check if the sharp tails should be extended higher
|
||||
bool detect_first_sharp_tail_only = false;
|
||||
const coordf_t extrusion_width = m_object_config->line_width.get_abs_value(object.print()->config().nozzle_diameter.get_at(object.config().support_interface_filament-1));
|
||||
const coordf_t extrusion_width = m_object_config->line_width.get_abs_value(nozzle_diameter_for_filament(object.print()->config(), object.config().support_interface_filament, object.print()->is_BBL_printer()));
|
||||
const coordf_t extrusion_width_scaled = scale_(extrusion_width);
|
||||
if (is_auto(m_object_config->support_type.value) && g_config_support_sharp_tails && !detect_first_sharp_tail_only) {
|
||||
for (size_t layer_nr = layer_id_start; layer_nr < num_layers; layer_nr++) {
|
||||
|
||||
@@ -1399,7 +1399,7 @@ void TreeSupport::generate_toolpaths()
|
||||
{
|
||||
const PrintObjectConfig &object_config = m_object->config();
|
||||
coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
|
||||
coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(object_config.support_filament - 1);
|
||||
coordf_t nozzle_diameter = nozzle_diameter_for_filament(*m_print_config, object_config.support_filament, m_object->print()->is_BBL_printer());
|
||||
coordf_t layer_height = object_config.layer_height.value;
|
||||
const size_t wall_count = object_config.tree_support_wall_count.value;
|
||||
|
||||
|
||||
@@ -17,9 +17,6 @@ typedef std::vector<Color> ColorList;
|
||||
typedef std::array<double, 3> ColorDouble;
|
||||
typedef std::array<std::size_t, 3> RGB;
|
||||
|
||||
// Function pointer type that points to a specific color-difference function based on the chosen method.
|
||||
using DistanceFunction = double (*)(const Color&, const Color&);
|
||||
|
||||
// Color space used for computing color differences.
|
||||
enum struct ColorDifferenceMethod : std::size_t {
|
||||
RGB = 0, // Simplest and fastest
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
namespace Slic3r { namespace tex2color {
|
||||
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
// Default upper bound on the number of half-edges in any single boundary cycle
|
||||
// that CloseBoundariesAndRepairManifoldness will attempt to triangulate. The
|
||||
// cost of triangulate_hole grows non-linearly with cycle length, so this caps
|
||||
@@ -61,6 +59,7 @@ struct BoundaryEdgeStats
|
||||
// any pre-processing (e.g. stitch_borders) needed for the count to be meaningful.
|
||||
inline BoundaryEdgeStats ComputeBoundaryEdgeStats(const cgalutils::CGALMesh& cgal_mesh)
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
using CGALMesh = cgalutils::CGALMesh;
|
||||
using HalfedgeDescriptor = boost::graph_traits<CGALMesh>::halfedge_descriptor;
|
||||
|
||||
@@ -87,6 +86,7 @@ inline BoundaryEdgeStats ComputeBoundaryEdgeStats(const cgalutils::CGALMesh& cga
|
||||
// boundary statistics; entering this function always triggers triangulation.
|
||||
inline void CloseBoundariesAndRepairManifoldness(cgalutils::CGALMesh& cgal_mesh)
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
using CGALMesh = cgalutils::CGALMesh;
|
||||
using HalfedgeDescriptor = boost::graph_traits<CGALMesh>::halfedge_descriptor;
|
||||
using FaceDescriptor = boost::graph_traits<CGALMesh>::face_descriptor;
|
||||
@@ -112,6 +112,7 @@ inline bool RepairMesh(const TriMesh& mesh,
|
||||
AlgoCancelCallback cancel_callback = nullptr,
|
||||
const RepairSetting& setting = RepairSetting{})
|
||||
{
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
using Clock = std::chrono::steady_clock;
|
||||
auto elapsed_ms = [](Clock::time_point t0) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - t0).count();
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include <initializer_list>
|
||||
#include <string_view>
|
||||
#include <regex>
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@@ -432,8 +432,6 @@ inline IntegerOnly<I, I> fast_round_up(double a)
|
||||
return a == 0.49999999999999994 ? I(0) : I(floor(a + 0.5));
|
||||
}
|
||||
|
||||
template<class T> using SamePair = std::pair<T, T>;
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1012,7 +1012,10 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
|
||||
float machine_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
|
||||
if (machine_diameter == 0.0f && preset_bundle) {
|
||||
const ConfigOption *opt = preset_bundle->printers.get_selected_preset().config.option("nozzle_diameter");
|
||||
if (opt) machine_diameter = static_cast<const ConfigOptionFloats *>(opt)->values[0];
|
||||
if (opt) {
|
||||
const auto &nd = static_cast<const ConfigOptionFloats *>(opt)->values;
|
||||
if (!nd.empty()) machine_diameter = nd.size() > 1 ? nd[1] : nd[0];
|
||||
}
|
||||
}
|
||||
stream << std::fixed << std::setprecision(1) << machine_diameter;
|
||||
std::string nozzle_diameter_str = stream.str();
|
||||
@@ -1291,7 +1294,10 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
float machine_diameter = obj->GetExtderSystem()->GetNozzleDiameter(0);
|
||||
if (machine_diameter == 0.0f) {
|
||||
const ConfigOption *opt = preset_bundle->printers.get_selected_preset().config.option("nozzle_diameter");
|
||||
if (opt) machine_diameter = static_cast<const ConfigOptionFloats *>(opt)->values[0];
|
||||
if (opt) {
|
||||
const auto &nd = static_cast<const ConfigOptionFloats *>(opt)->values;
|
||||
if (!nd.empty()) machine_diameter = nd.size() > 1 ? nd[1] : nd[0];
|
||||
}
|
||||
}
|
||||
stream << std::fixed << std::setprecision(1) << machine_diameter;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -30,7 +29,6 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
@@ -19,20 +19,17 @@
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/panel.h>
|
||||
@@ -36,14 +35,12 @@
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/gbsizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/webrequest.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <wx/hyperlink.h>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <wx/hyperlink.h>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <wx/string.h>
|
||||
#include <wx/toplevel.h>
|
||||
#include <wx/timer.h>
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "CapsuleButton.hpp"
|
||||
|
||||
namespace Slic3r::GUI { struct IntEvent; }
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "Jobs/BoostThreadWorker.hpp"
|
||||
#include "Jobs/PlaterWorker.hpp"
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/Utils/Bonjour.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/DialogButtons.hpp"
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -24,7 +23,6 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// #include "libslic3r/GCodeSender.hpp"
|
||||
#include "ConfigManipulation.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
@@ -872,10 +871,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
toggle_line("center_of_surface_pattern", has_centered_surface);
|
||||
|
||||
// Orca: separate infills
|
||||
bool is_internal_infill_separable = is_separable_infill_pattern(config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value) ||
|
||||
config->opt_string("sparse_infill_rotate_template") != "" ||
|
||||
config->opt_string("solid_infill_rotate_template") != "";
|
||||
toggle_line("separated_infills", is_internal_infill_separable);
|
||||
toggle_line("separated_infills", is_separable_infill_pattern(pattern));
|
||||
|
||||
// Fill order is only meaningful for the center-based surface fill patterns; hide it otherwise.
|
||||
auto is_centered_fill = [](InfillPattern p) { return p == ipConcentric || p == ipSpiralInset || p == ipArchimedeanChords || p == ipOctagramSpiral; };
|
||||
|
||||
@@ -198,6 +198,3 @@ template<> struct std::hash<NozzleDef>
|
||||
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
||||
};
|
||||
};
|
||||
|
||||
// key(extruder_id) -> { key1(nozzle type info), val1( number of the nozzle type)}
|
||||
using ExtruderNozzleInfos = std::unordered_map<int, std::unordered_map<NozzleDef, int>>;
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
#include <wx/colour.h>
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "DeviceErrorDialog.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "ReleaseNote.hpp"
|
||||
#include <string>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
@@ -2427,11 +2427,15 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
if (m_canvas_type == ECanvasType::CanvasView3D) {
|
||||
// m_show_bed gates the plate list too: hiding the bed but leaving its grid and outline
|
||||
// floating would read as a rendering fault rather than a deliberate view option.
|
||||
// Design tab: while its reference planes are up they draw their own axes from the modeling
|
||||
// origin, where the bed's triad would otherwise sit on top of them.
|
||||
if (show_bed)
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(),
|
||||
m_show_world_axes && !(m_design_sketch_tool != nullptr && m_design_sketch_tool->draws_reference_axes()));
|
||||
if (show_bed) {
|
||||
bool show_axes = m_show_world_axes;
|
||||
#ifdef SLIC3R_CAD
|
||||
// Design tab: while its reference planes are up they draw their own axes from the modeling
|
||||
// origin, where the bed's triad would otherwise sit on top of them.
|
||||
show_axes = show_axes && !(m_design_sketch_tool != nullptr && m_design_sketch_tool->draws_reference_axes());
|
||||
#endif
|
||||
_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
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
|
||||
|
||||
@@ -183,13 +183,10 @@ wxDECLARE_EVENT(EVT_GLCANVAS_PLATE_NAME_CHANGE, SimpleEvent);
|
||||
//BBS: declare EVT_GLCANVAS_PLATE_SELECT
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_PLATE_SELECT, SimpleEvent);
|
||||
|
||||
using Vec2dEvent = Event<Vec2d>;
|
||||
// _bool_ value is used as a indicator of selection in the 3DScene
|
||||
using RBtnEvent = Event<std::pair<Vec2d, bool>>;
|
||||
using RBtnPlateEvent = Event<std::pair<Vec2d, int>>;
|
||||
template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
|
||||
|
||||
using Vec3dEvent = Event<Vec3d>;
|
||||
template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
|
||||
|
||||
using HeightProfileSmoothEvent = Event<HeightProfileSmoothingParams>;
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <wx/dcgraph.h>
|
||||
#include <wx/dcmemory.h>
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
#include "AboutDialog.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
#include "WebUserLoginDialog.hpp"
|
||||
|
||||
|
||||
@@ -172,12 +172,10 @@
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/I18N.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/InstanceLock.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "slic3r/plugin/host/PluginHostUi.hpp"
|
||||
#include "slic3r/plugin/PythonInterpreter.hpp"
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
#include <nanosvg/nanosvg.h>
|
||||
#include <nanosvg/nanosvgrast.h>
|
||||
#include "OpenGLManager.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
@@ -113,7 +113,6 @@
|
||||
#include "MsgDialog.hpp"
|
||||
#include "Notebook.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "NotificationManager.hpp"
|
||||
#include "MarkdownTip.hpp"
|
||||
#include "NetworkTestDialog.hpp"
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "wx/evtloop.h"
|
||||
|
||||
#include "MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
#include "MainFrame.hpp"
|
||||
#include "MediaPlayCtrl.h"
|
||||
#include "MediaFilePanel.h"
|
||||
#include "Plater.hpp"
|
||||
#include "BindDialog.hpp"
|
||||
|
||||
#include "DeviceCore/DevManager.h"
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/panel.h>
|
||||
@@ -31,14 +30,12 @@
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/gbsizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/tglbtn.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/webrequest.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "libslic3r_version.h"
|
||||
#include "slic3r/Utils/Http.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
|
||||
@@ -54,7 +54,6 @@ struct Option {
|
||||
Option(const ConfigOptionDef& _opt, t_config_option_key id) :
|
||||
opt(_opt), opt_id(id) {}
|
||||
};
|
||||
using t_option = std::unique_ptr<Option>; //!
|
||||
|
||||
/// Represents option lines
|
||||
class Line : public UndoValueUIManager
|
||||
|
||||
+21
-21
@@ -1862,27 +1862,22 @@ bool Sidebar::priv::switch_diameter(bool single)
|
||||
auto diameter_left = left_extruder->combo_diameter->GetValue();
|
||||
auto diameter_right = right_extruder->combo_diameter->GetValue();
|
||||
if (diameter_left != diameter_right) {
|
||||
std::string printer_type = wxGetApp().preset_bundle->printers.get_edited_preset().get_printer_type(wxGetApp().preset_bundle);
|
||||
auto left_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::SentenceCase));
|
||||
auto right_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::SentenceCase));
|
||||
MessageDialog dlg(this->plater,
|
||||
_L("The software does not support using different diameter of nozzles for one print. "
|
||||
"If the left and right nozzles are inconsistent, we can only proceed with single-head printing. "
|
||||
"Please confirm which nozzle you would like to use for this project."),
|
||||
_L("Switch diameter"), wxYES_NO | wxNO_DEFAULT);
|
||||
dlg.SetButtonLabel(wxID_YES, wxString::Format("%s: %smm", left_name, diameter_left));
|
||||
dlg.SetButtonLabel(wxID_NO, wxString::Format("%s: %smm", right_name, diameter_right));
|
||||
int result = dlg.ShowModal();
|
||||
if (result == wxID_YES)
|
||||
diameter = diameter_left;
|
||||
else if (result == wxID_NO)
|
||||
diameter = diameter_right;
|
||||
else
|
||||
double left_value = 0.0, right_value = 0.0;
|
||||
if (!diameter_left.ToCDouble(&left_value) || !diameter_right.ToCDouble(&right_value))
|
||||
return false;
|
||||
|
||||
Tab* printer_tab = wxGetApp().get_tab(Preset::TYPE_PRINTER);
|
||||
DynamicPrintConfig new_conf = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
auto* nozzle_diameter_opt = new_conf.option<ConfigOptionFloats>("nozzle_diameter");
|
||||
if (printer_tab == nullptr || nozzle_diameter_opt == nullptr || nozzle_diameter_opt->size() < 2)
|
||||
return false;
|
||||
|
||||
nozzle_diameter_opt->values[0] = left_value;
|
||||
nozzle_diameter_opt->values[1] = right_value;
|
||||
printer_tab->load_config(new_conf);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
diameter = diameter_left;
|
||||
}
|
||||
diameter = diameter_left;
|
||||
}
|
||||
|
||||
return switch_diameter_to(diameter);
|
||||
@@ -1892,15 +1887,20 @@ bool Sidebar::priv::switch_diameter_to(const wxString &diameter)
|
||||
{
|
||||
// ORCA: Check if the selected diameter matches the current nozzle diameter in the config
|
||||
Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
|
||||
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printer_preset.config.option("nozzle_diameter"));
|
||||
// ORCA: the left/right combos of a BBL multi-nozzle printer set the nozzles apart without leaving
|
||||
// the preset (see switch_diameter), so there the preset is only kept while every nozzle matches.
|
||||
const bool nozzles_apart = nozzle_diameter && nozzle_diameter->size() > 1 && wxGetApp().preset_bundle->is_bbl_vendor() &&
|
||||
std::any_of(nozzle_diameter->values.begin(), nozzle_diameter->values.end(),
|
||||
[&diameter](double value) { return get_diameter_string(value) != diameter.ToStdString(); });
|
||||
// The combo lists printer variants, and the variant of a mixed-nozzle machine ("0.4+0.6") is no
|
||||
// single extruder's diameter, so the preset's own variant answers first.
|
||||
const std::string &printer_variant = printer_preset.config.opt_string("printer_variant");
|
||||
if (printer_variant == diameter.ToStdString()) {
|
||||
if (printer_variant == diameter.ToStdString() && !nozzles_apart) {
|
||||
return true;
|
||||
}
|
||||
// A named variant ("0.4 High Flow") shares its diameter with the standard profile, which selecting
|
||||
// the plain diameter switches back to, so only a preset naming no variant is kept by its diameter.
|
||||
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printer_preset.config.option("nozzle_diameter"));
|
||||
if (printer_variant.empty() && nozzle_diameter && nozzle_diameter->size() > 0) {
|
||||
auto current_nozzle_dia = get_diameter_string(nozzle_diameter->values[0]);
|
||||
// If the selected diameter is the same as current nozzle, don't switch profiles
|
||||
|
||||
@@ -108,7 +108,6 @@ class PlaterPresetComboBox;
|
||||
class PartPlateList;
|
||||
class SyncNozzleAndAmsDialog;
|
||||
class FinishSyncAmsDialog;
|
||||
using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
|
||||
|
||||
class Plater;
|
||||
enum class ActionButtonType : int;
|
||||
@@ -325,7 +324,6 @@ private:
|
||||
class Plater: public wxPanel
|
||||
{
|
||||
public:
|
||||
using fs_path = boost::filesystem::path;
|
||||
|
||||
Plater(wxWindow *parent, MainFrame *main_frame);
|
||||
Plater(Plater &&) = delete;
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "MainFrame.hpp"
|
||||
#include <miniz.h>
|
||||
#include <OrcaCloudServiceAgent.hpp>
|
||||
#include <wx/event.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
#include "libslic3r_version.h"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "Jobs/ProgressIndicator.hpp"
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -32,10 +31,8 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/hyperlink.h>
|
||||
#include <wx/richtext/richtextctrl.h>
|
||||
|
||||
#include "AmsMappingPopup.hpp"
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -38,7 +37,6 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
#include <wx/srchctrl.h>
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -29,7 +28,6 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
#include <wx/srchctrl.h>
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/dataview.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
@@ -33,7 +32,6 @@
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/popupwin.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/artprov.h>
|
||||
#include <wx/wrapsizer.h>
|
||||
#include <wx/srchctrl.h>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// #include "libslic3r/GCodeSender.hpp"
|
||||
//#include "slic3r/Utils/Serial.hpp"
|
||||
#include "Tab.hpp"
|
||||
#include "PresetHints.hpp"
|
||||
@@ -102,8 +101,6 @@
|
||||
#include "Widgets/MultiNozzleSync.hpp"
|
||||
#include "Widgets/SwitchButton.hpp"
|
||||
#include "Widgets/TabCtrl.hpp"
|
||||
#include "Widgets/ComboBox.hpp"
|
||||
#include "Search.hpp"
|
||||
#include "BedShapeDialog.hpp"
|
||||
#include "libslic3r/GCode/Thumbnails.hpp"
|
||||
#include "WipeTowerDialog.hpp"
|
||||
@@ -5836,7 +5833,8 @@ if (is_marlin_flavor)
|
||||
// The page edits the extruder selected on the variant switch.
|
||||
const size_t extruder_idx = size_t(get_current_active_extruder());
|
||||
bool is_SEMM = m_config->opt_bool("single_extruder_multi_material");
|
||||
if (is_SEMM && m_extruders_count > 1 && boost::starts_with(opt_key, "nozzle_diameter"))
|
||||
bool independent_nozzles = m_preset_bundle && m_preset_bundle->is_bbl_vendor() && m_extruders_count > 1;
|
||||
if (is_SEMM && m_extruders_count > 1 && !independent_nozzles && boost::starts_with(opt_key, "nozzle_diameter"))
|
||||
{
|
||||
SuppressBackgroundProcessingUpdate sbpu;
|
||||
const double new_nd = boost::any_cast<double>(value);
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
#include "../GUI/DeviceCore/DevConfig.h"
|
||||
#include "../GUI/DeviceCore/DevExtruderSystem.h"
|
||||
#include "../GUI/DeviceCore/DevManager.h"
|
||||
#include "../GUI/DeviceCore/DevStorage.h"
|
||||
#include "libslic3r/FlushVolCalc.hpp"
|
||||
#include "../GUI/Plater.hpp"
|
||||
#include <memory>
|
||||
#include "slic3r/GUI/Jobs/Worker.hpp"
|
||||
|
||||
@@ -365,7 +365,6 @@ void StyleManager::init_trunc_names(float max_width) {
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
|
||||
// for get DPI
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp"
|
||||
|
||||
@@ -554,8 +553,6 @@ bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr<FontFile>
|
||||
return true;
|
||||
}
|
||||
|
||||
#include <libslic3r/AppConfig.hpp>
|
||||
#include "WxFontUtils.hpp"
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
// StylesSerializable
|
||||
|
||||
@@ -89,7 +89,6 @@ using fn_ft_job_get_result = ft_err(FT_CALL *)(FT_JobHandle *, uint32_t timeo
|
||||
using fn_ft_tunnel_start_job = ft_err(FT_CALL *)(FT_TunnelHandle *, FT_JobHandle *);
|
||||
using fn_ft_job_cancel = ft_err(FT_CALL *)(FT_JobHandle *);
|
||||
|
||||
using fn_ft_job_msg_destroy = void(FT_CALL *)(ft_job_msg *);
|
||||
using fn_ft_job_set_msg_cb = ft_err(FT_CALL *)(FT_JobHandle *, void(FT_CALL *)(void *user, ft_job_msg msg), void *user);
|
||||
using fn_ft_job_try_get_msg = ft_err(FT_CALL *)(FT_JobHandle *, ft_job_msg *out_msg);
|
||||
using fn_ft_job_get_msg = ft_err(FT_CALL *)(FT_JobHandle *, uint32_t timeout_ms, ft_job_msg *out_msg);
|
||||
|
||||
@@ -441,7 +441,19 @@ int MoonrakerPrinterAgent::set_queue_on_main_fn(QueueOnMainFn fn)
|
||||
|
||||
void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays)
|
||||
{
|
||||
|
||||
// Look up MachineObject via DeviceManager
|
||||
auto* dev_manager = GUI::wxGetApp().getDeviceManager();
|
||||
if (!dev_manager) {
|
||||
return;
|
||||
}
|
||||
MachineObject* obj = dev_manager->get_my_machine(device_info.dev_id);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build BBL-format JSON for DevFilaSystemParser::ParseV1_0
|
||||
nlohmann::json ams_json = nlohmann::json::object();
|
||||
nlohmann::json ams_array = nlohmann::json::array();
|
||||
|
||||
// Calculate ams_exist_bits and tray_exist_bits
|
||||
@@ -449,9 +461,11 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
|
||||
unsigned long tray_exist_bits = 0;
|
||||
|
||||
for (int ams_id = 0; ams_id < ams_count; ++ams_id) {
|
||||
if (ams_id < 32) {
|
||||
ams_exist_bits |= (1UL << ams_id);
|
||||
}
|
||||
ams_exist_bits |= (1 << ams_id);
|
||||
|
||||
nlohmann::json ams_unit = nlohmann::json::object();
|
||||
ams_unit["id"] = std::to_string(ams_id);
|
||||
ams_unit["info"] = "0002"; // treat as AMS_LITE
|
||||
|
||||
nlohmann::json tray_array = nlohmann::json::array();
|
||||
int max_slot_in_this_ams = std::min(3, max_lane_index - ams_id * 4);
|
||||
@@ -467,127 +481,43 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
|
||||
}
|
||||
}
|
||||
|
||||
if (tray && tray->has_filament && slot_index < 32) {
|
||||
tray_exist_bits |= (1UL << slot_index);
|
||||
nlohmann::json tray_json = nlohmann::json::object();
|
||||
tray_json["id"] = std::to_string(slot_id);
|
||||
tray_json["tag_uid"] = "0000000000000000";
|
||||
|
||||
if (tray && tray->has_filament) {
|
||||
tray_exist_bits |= (1 << slot_index);
|
||||
|
||||
tray_json["tray_info_idx"] = tray->tray_info_idx;
|
||||
tray_json["tray_type"] = tray->tray_type;
|
||||
tray_json["tray_color"] = normalize_color_value(tray->tray_color);
|
||||
|
||||
// Add temperature data if provided
|
||||
if (tray->bed_temp > 0) {
|
||||
tray_json["bed_temp"] = std::to_string(tray->bed_temp);
|
||||
}
|
||||
if (tray->nozzle_temp > 0) {
|
||||
tray_json["nozzle_temp_max"] = std::to_string(tray->nozzle_temp);
|
||||
}
|
||||
} else {
|
||||
tray_json["tray_info_idx"] = "";
|
||||
tray_json["tray_type"] = "";
|
||||
tray_json["tray_color"] = "00000000";
|
||||
tray_json["tray_slot_placeholder"] = "1";
|
||||
}
|
||||
|
||||
static const AmsTrayData empty_tray;
|
||||
tray_array.push_back(make_ams_tray_json(std::to_string(slot_id), tray ? *tray : empty_tray));
|
||||
tray_array.push_back(tray_json);
|
||||
}
|
||||
|
||||
nlohmann::json ams_unit = nlohmann::json::object();
|
||||
ams_unit["id"] = std::to_string(ams_id);
|
||||
ams_unit["info"] = "0002"; // treat as AMS_LITE
|
||||
ams_unit["tray"] = tray_array;
|
||||
ams_array.push_back(ams_unit);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload: Parsed " << trays.size() << " trays";
|
||||
submit_ams_payload(ams_array, ams_exist_bits, tray_exist_bits);
|
||||
}
|
||||
|
||||
void MoonrakerPrinterAgent::build_ams_payload_grouped(const std::vector<AmsTrayData>& trays)
|
||||
{
|
||||
// One AMS box per extruder, best effort. Lane-data changers (AFC, recent Happy Hare) report an
|
||||
// extruder per lane but no physical box layout, so a box here is a logical feed group, not a
|
||||
// discovered unit. Lanes without an extruder fall back to the main extruder.
|
||||
std::map<int, std::vector<const AmsTrayData*>> boxes;
|
||||
for (const auto& tray : trays) {
|
||||
boxes[tray.extruder_id].push_back(&tray);
|
||||
}
|
||||
|
||||
nlohmann::json ams_array = nlohmann::json::array();
|
||||
unsigned long ams_exist_bits = 0;
|
||||
unsigned long tray_exist_bits = 0;
|
||||
|
||||
int ams_id = 0;
|
||||
for (auto& [extruder_id, lanes] : boxes) {
|
||||
if (ams_id < 32) {
|
||||
ams_exist_bits |= (1UL << ams_id);
|
||||
}
|
||||
|
||||
// Keep a deterministic slot order within the box.
|
||||
std::stable_sort(lanes.begin(), lanes.end(),
|
||||
[](const AmsTrayData* a, const AmsTrayData* b) { return a->slot_index < b->slot_index; });
|
||||
|
||||
nlohmann::json tray_array = nlohmann::json::array();
|
||||
for (size_t slot = 0; slot < lanes.size(); ++slot) {
|
||||
const AmsTrayData& tray = *lanes[slot];
|
||||
int bit = ams_id * 4 + static_cast<int>(slot);
|
||||
if (tray.has_filament && bit < 32) {
|
||||
tray_exist_bits |= (1UL << bit);
|
||||
}
|
||||
tray_array.push_back(make_ams_tray_json(std::to_string(slot), tray));
|
||||
}
|
||||
|
||||
nlohmann::json ams_unit = nlohmann::json::object();
|
||||
ams_unit["id"] = std::to_string(ams_id);
|
||||
// info: bits 0-3 = unit type (2 = AMS_LITE), bits 8-11 = bound extruder id.
|
||||
const int info_val = 2 | ((extruder_id & 0x0F) << 8);
|
||||
const char* hex_digits = "0123456789ABCDEF";
|
||||
std::string info = "0000";
|
||||
info[3] = hex_digits[info_val & 0x0F];
|
||||
info[2] = hex_digits[(info_val >> 4) & 0x0F];
|
||||
info[1] = hex_digits[(info_val >> 8) & 0x0F];
|
||||
info[0] = hex_digits[(info_val >> 12) & 0x0F];
|
||||
ams_unit["info"] = info;
|
||||
ams_unit["tray"] = tray_array;
|
||||
ams_array.push_back(ams_unit);
|
||||
++ams_id;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload_grouped: Parsed " << trays.size()
|
||||
<< " trays in " << boxes.size() << " extruder box(es)";
|
||||
submit_ams_payload(ams_array, ams_exist_bits, tray_exist_bits);
|
||||
}
|
||||
|
||||
nlohmann::json MoonrakerPrinterAgent::make_ams_tray_json(const std::string& tray_id, const AmsTrayData& tray)
|
||||
{
|
||||
nlohmann::json tray_json = nlohmann::json::object();
|
||||
tray_json["id"] = tray_id;
|
||||
tray_json["tag_uid"] = "0000000000000000";
|
||||
|
||||
if (tray.has_filament) {
|
||||
tray_json["tray_info_idx"] = tray.tray_info_idx;
|
||||
tray_json["tray_type"] = tray.tray_type;
|
||||
tray_json["tray_color"] = normalize_color_value(tray.tray_color);
|
||||
|
||||
// Add temperature data if provided
|
||||
if (tray.bed_temp > 0) {
|
||||
tray_json["bed_temp"] = std::to_string(tray.bed_temp);
|
||||
}
|
||||
if (tray.nozzle_temp > 0) {
|
||||
tray_json["nozzle_temp_max"] = std::to_string(tray.nozzle_temp);
|
||||
}
|
||||
} else {
|
||||
tray_json["tray_info_idx"] = "";
|
||||
tray_json["tray_type"] = "";
|
||||
tray_json["tray_color"] = "00000000";
|
||||
tray_json["tray_slot_placeholder"] = "1";
|
||||
}
|
||||
|
||||
return tray_json;
|
||||
}
|
||||
|
||||
void MoonrakerPrinterAgent::submit_ams_payload(const nlohmann::json& ams_array, unsigned long ams_exist_bits, unsigned long tray_exist_bits)
|
||||
{
|
||||
// Look up MachineObject via DeviceManager
|
||||
auto* dev_manager = GUI::wxGetApp().getDeviceManager();
|
||||
if (!dev_manager) {
|
||||
return;
|
||||
}
|
||||
MachineObject* obj = dev_manager->get_my_machine(device_info.dev_id);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Format as hex strings (matching BBL protocol)
|
||||
std::ostringstream ams_exist_ss;
|
||||
ams_exist_ss << std::hex << std::uppercase << ams_exist_bits;
|
||||
std::ostringstream tray_exist_ss;
|
||||
tray_exist_ss << std::hex << std::uppercase << tray_exist_bits;
|
||||
|
||||
nlohmann::json ams_json = nlohmann::json::object();
|
||||
ams_json["ams"] = ams_array;
|
||||
ams_json["ams_exist_bits"] = ams_exist_ss.str();
|
||||
ams_json["tray_exist_bits"] = tray_exist_ss.str();
|
||||
@@ -598,6 +528,7 @@ void MoonrakerPrinterAgent::submit_ams_payload(const nlohmann::json& ams_array,
|
||||
|
||||
// Call the parser to populate DevFilaSystem
|
||||
DevFilaSystemParser::ParseV1_0(print_json, obj, obj->GetFilaSystem().get(), false);
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload: Parsed " << trays.size() << " trays";
|
||||
|
||||
// Set printer_type so update_sync_status() can match it against the preset's printer type.
|
||||
// Without this, the comparison fails and all sync badges are cleared.
|
||||
@@ -637,15 +568,17 @@ bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id, FilamentSync
|
||||
if (fetch_moonraker_filament_data(trays, max_lane_index)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Moonraker filament system with "
|
||||
<< (max_lane_index + 1) << " lanes";
|
||||
build_ams_payload_grouped(trays);
|
||||
int ams_count = (max_lane_index + 4) / 4;
|
||||
build_ams_payload(ams_count, max_lane_index, trays);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fall back to the Happy Hare `mmu` object for HH builds that do not publish lane_data
|
||||
// Attempt Happy Hare first (more widely adopted, supports more filament changers)
|
||||
if (fetch_hh_filament_info(trays, max_lane_index)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Happy Hare MMU with "
|
||||
<< (max_lane_index + 1) << " gates";
|
||||
build_ams_payload_grouped(trays);
|
||||
int ams_count = (max_lane_index + 4) / 4;
|
||||
build_ams_payload(ams_count, max_lane_index, trays);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -751,30 +684,6 @@ int MoonrakerPrinterAgent::safe_json_int(const nlohmann::json& obj, const char*
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MoonrakerPrinterAgent::safe_json_int_flexible(const nlohmann::json& obj, const char* key, int default_value)
|
||||
{
|
||||
auto it = obj.find(key);
|
||||
if (it == obj.end())
|
||||
return default_value;
|
||||
if (it->is_number_integer())
|
||||
return it->get<int>();
|
||||
if (it->is_string()) {
|
||||
const std::string s = it->get<std::string>();
|
||||
if (s.empty())
|
||||
return default_value;
|
||||
try {
|
||||
std::size_t consumed = 0;
|
||||
const int value = std::stoi(s, &consumed);
|
||||
if (consumed != s.size()) // reject partial parses such as "2oops"
|
||||
return default_value;
|
||||
return value;
|
||||
} catch (...) {
|
||||
return default_value;
|
||||
}
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
std::string MoonrakerPrinterAgent::safe_array_string(const nlohmann::json& arr, int idx)
|
||||
{
|
||||
if (arr.is_array() && idx >= 0 && idx < static_cast<int>(arr.size()) && arr[idx].is_string())
|
||||
@@ -883,22 +792,23 @@ bool MoonrakerPrinterAgent::fetch_moonraker_filament_data(std::vector<AmsTrayDat
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lane tool number (0-based). AFC reports it as a string in the documented sample but it
|
||||
// can also be a number; accept either and skip lanes without a usable index.
|
||||
const int lane_index = safe_json_int_flexible(lane_obj, "lane", -1);
|
||||
// Extract lane index from the "lane" field (tool number, 0-based)
|
||||
std::string lane_str = safe_json_string(lane_obj, "lane");
|
||||
int lane_index = -1;
|
||||
if (!lane_str.empty()) {
|
||||
try {
|
||||
lane_index = std::stoi(lane_str);
|
||||
} catch (...) {
|
||||
lane_index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lane_index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AmsTrayData tray;
|
||||
tray.slot_index = lane_index;
|
||||
// AFC exposes the lane's extruder/toolhead as "extruder_index"; tolerate "extruder_id" as
|
||||
// an alias. Unknown or negative falls back to the main extruder (best effort).
|
||||
tray.extruder_id = safe_json_int_flexible(lane_obj, "extruder_index",
|
||||
safe_json_int_flexible(lane_obj, "extruder_id", 0));
|
||||
if (tray.extruder_id < 0) {
|
||||
tray.extruder_id = 0;
|
||||
}
|
||||
tray.tray_color = safe_json_string(lane_obj, "color");
|
||||
tray.tray_type = safe_json_string(lane_obj, "material");
|
||||
tray.bed_temp = safe_json_int(lane_obj, "bed_temp");
|
||||
@@ -1003,41 +913,48 @@ bool MoonrakerPrinterAgent::fetch_hh_filament_info(std::vector<AmsTrayData>& tra
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse gate data. Every gate becomes a lane so empty/unknown gates surface
|
||||
// as empty AMS slots rather than vanishing. HH is single-extruder, so all
|
||||
// lanes belong to feed group 0.
|
||||
// Parse gate data
|
||||
trays.clear();
|
||||
max_lane_index = 0;
|
||||
|
||||
for (int gate_idx = 0; gate_idx < num_gates; ++gate_idx) {
|
||||
// Check gate_status: -1 = unknown, 0 = empty, 1 or 2 = available
|
||||
int status = safe_array_int(gate_status, gate_idx);
|
||||
if (status <= 0) {
|
||||
continue; // Skip unknown or empty gates
|
||||
}
|
||||
|
||||
// Extract gate data
|
||||
std::string material = safe_array_string(gate_material, gate_idx);
|
||||
std::string color = safe_array_string(gate_color, gate_idx);
|
||||
int nozzle_temp = safe_array_int(gate_temperature, gate_idx);
|
||||
|
||||
AmsTrayData tray;
|
||||
tray.slot_index = gate_idx;
|
||||
tray.extruder_id = 0;
|
||||
|
||||
if (status > 0 && !material.empty()) {
|
||||
tray.tray_type = material;
|
||||
tray.tray_color = color;
|
||||
tray.nozzle_temp = nozzle_temp;
|
||||
tray.bed_temp = 0; // HH doesn't provide bed temp in gate arrays
|
||||
tray.has_filament = true;
|
||||
|
||||
auto* bundle = GUI::wxGetApp().preset_bundle;
|
||||
tray.tray_info_idx = bundle
|
||||
? bundle->filaments.filament_id_by_type(tray.tray_type)
|
||||
: map_filament_type_to_generic_id(tray.tray_type);
|
||||
} else {
|
||||
tray.has_filament = false;
|
||||
// Skip if no material type (empty gate)
|
||||
if (material.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AmsTrayData tray;
|
||||
tray.slot_index = gate_idx;
|
||||
tray.tray_type = material;
|
||||
tray.tray_color = color;
|
||||
tray.nozzle_temp = nozzle_temp;
|
||||
tray.bed_temp = 0; // HH doesn't provide bed temp in gate arrays
|
||||
tray.has_filament = true;
|
||||
|
||||
auto* bundle = GUI::wxGetApp().preset_bundle;
|
||||
tray.tray_info_idx = bundle
|
||||
? bundle->filaments.filament_id_by_type(tray.tray_type)
|
||||
: map_filament_type_to_generic_id(tray.tray_type);
|
||||
|
||||
max_lane_index = std::max(max_lane_index, gate_idx);
|
||||
trays.push_back(tray);
|
||||
}
|
||||
|
||||
max_lane_index = num_gates - 1;
|
||||
if (trays.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_hh_filament_info: No valid HH gates found";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ protected:
|
||||
// Tray data for AMS payload building
|
||||
struct AmsTrayData {
|
||||
int slot_index = 0; // 0-based slot index
|
||||
int extruder_id = 0; // Extruder this lane feeds (AFC extruder_index); 0 when unknown
|
||||
bool has_filament = false;
|
||||
std::string tray_type; // Material type (e.g., "PLA", "ASA")
|
||||
std::string tray_color; // Raw color (#RRGGBB, 0xRRGGBB, or RRGGBBAA)
|
||||
@@ -100,10 +99,6 @@ protected:
|
||||
// Build ams JSON and call parser
|
||||
void build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays);
|
||||
|
||||
// Build ams JSON with one box per extruder (best effort, for lane_data-based filament
|
||||
// changers such as AFC) and call the parser. Boxes are ordered by extruder id.
|
||||
void build_ams_payload_grouped(const std::vector<AmsTrayData>& trays);
|
||||
|
||||
// Methods that derived classes may need to override or access
|
||||
virtual bool init_device_info(const std::string& dev_id, const std::string& dev_ip, const std::string& username, const std::string& password, bool use_ssl, const std::string& port);
|
||||
virtual bool fetch_device_info(const std::string& base_url, const std::string& api_key, MoonrakerDeviceInfo& info, std::string& error) const;
|
||||
@@ -169,18 +164,9 @@ private:
|
||||
bool fetch_hh_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
||||
bool fetch_moonraker_filament_data(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
||||
|
||||
// Build one BBL tray JSON object (placeholder when the tray has no filament).
|
||||
static nlohmann::json make_ams_tray_json(const std::string& tray_id, const AmsTrayData& tray);
|
||||
|
||||
// Wrap the AMS boxes into the payload ParseV1_0 understands and apply the pull-mode
|
||||
// readiness state (printer_type, push counters, storage, module versions).
|
||||
void submit_ams_payload(const nlohmann::json& ams_array, unsigned long ams_exist_bits, unsigned long tray_exist_bits);
|
||||
|
||||
// JSON helper methods
|
||||
static std::string safe_json_string(const nlohmann::json& obj, const char* key);
|
||||
static int safe_json_int(const nlohmann::json& obj, const char* key);
|
||||
// Accepts either a JSON number or a numeric string (AFC reports some fields as strings).
|
||||
static int safe_json_int_flexible(const nlohmann::json& obj, const char* key, int default_value);
|
||||
static std::string safe_array_string(const nlohmann::json& arr, int idx);
|
||||
static int safe_array_int(const nlohmann::json& arr, int idx);
|
||||
static std::string normalize_color_value(const std::string& color);
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/UpdateDialogs.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user