CuraEngine(4)核心SliceDataStorage类和函数sliceModel源码阅读
CuraEngine所有的和模型切片相关的数据都存放在一个叫 SliceDataStorage
的类里面,这个SliceDataStorage类包含了一系列的成员变量,它们各自代表了3D打印切片过程中的不同数据和信息。
SliceDataStorage类
class SliceDataStorage : public NoCopy
{
public:
size_t print_layer_count; //!< The total number of layers (except the raft and filler layers)
Point3 model_size, model_min, model_max;
AABB3D machine_size; //!< The bounding box with the width, height and depth of the printer.
std::vector<SliceMeshStorage> meshes;
std::vector<WipeScriptConfig> wipe_config_per_extruder; //!< Wipe configs per extruder.
std::vector<RetractionConfig> retraction_config_per_extruder; //!< Retraction config per extruder.
std::vector<RetractionConfig> extruder_switch_retraction_config_per_extruder; //!< Retraction config per extruder for when performing an extruder switch
SupportStorage support;
Polygons skirt_brim[MAX_EXTRUDERS]; //!< Skirt and brim polygons per extruder, ordered from inner to outer polygons.
Polygons raftOutline; //Storage for the outline of the raft. Will be filled with lines when the GCode is generated.
int max_print_height_second_to_last_extruder; //!< Used in multi-extrusion: the layer number beyond which all models are printed with the same extruder
std::vector<int> max_print_height_per_extruder; //!< For each extruder the highest layer number at which it is used.
std::vector<size_t> max_print_height_order; //!< Ordered indices into max_print_height_per_extruder: back() will return the extruder number with the highest print height.
std::vector<int> spiralize_seam_vertex_indices; //!< the index of the seam vertex for each layer
std::vector<Polygons* > spiralize_wall_outlines; //!< the wall outline polygons for each layer
PrimeTower primeTower;
std::vector<Polygons> oozeShield; //oozeShield per layer
Polygons draft_protection_shield; //!< The polygons for a heightened skirt which protects from warping by gusts of wind and acts as a heated chamber.
/*!
* \brief Creates a new slice data storage that stores the slice data of the
* current mesh group.
*/
SliceDataStorage();
~SliceDataStorage()
{
}
下面是对这些成员变量的解释:
print_layer_count
: 整数类型,表示除了基础层(raft)和填充层之外的层数总数。
model_size, model_min, model_max
: Point3类型(可能是三维的点,包含x、y和z坐标)。分别表示模型的大小、最小边界和最大边界。
machine_size
: AABB3D类型(AABB是Axis-Aligned Bounding Box的缩写,即轴对齐的边界框)。它表示打印机的宽度、高度和深度的边界框。
meshes
: SliceMeshStorage类型的向量vector,存储了所有网格(三维mesh)的切片数据。这个最重要,后面附有SliceMeshStorage类
的定义
wipe_config_per_extruder
: WipeScriptConfig类型的向量,存储了每个挤出机的擦拭配置。
retraction_config_per_extruder
: RetractionConfig类型的向量,存储了每个挤出机的回抽配置。
extruder_switch_retraction_config_per_extruder
: RetractionConfig类型的向量,存储了在挤出机切换时需要使用的回抽配置。
support
: SupportStorage类型,可能用于存储打印过程中支撑结构的信息。
skirt_brim
: Polygons类型的数组,用于存储每个挤出机的裙边和边缘多边形,从内到外排序。
raftOutline
: Polygons类型,用于存储raft(基础层)的轮廓,将在生成G代码时填充线条。
max_print_height_second_to_last_extruder
: 整数类型,在多挤出机打印中使用,表示所有模型使用相同挤出机打印的层数界限。
max_print_height_per_extruder
: 整数类型的向量,对于每个挤出机,存储了使用它的最高层数。
max_print_height_order
: 整数类型的向量,是对max_print_height_per_extruder中元素的排序索引,通过back()方法可以获取具有最高打印高度的挤出机编号。
spiralize_seam_vertex_indices
: 整数类型的向量,存储了每层螺旋接缝顶点的索引。
spiralize_wall_outlines
: Polygons指针的向量,存储了每层的墙体外轮廓多边形。
primeTower: PrimeTower
类型,可能用于存储挤出机引丝塔的信息。
oozeShield
: Polygons类型的向量,存储了每层的防漏屏蔽多边形。
draft_protection_shield
: Polygons类型,表示一个加高的裙边,用于防止风吹造成的翘曲,并作为加热舱室。
这些成员变量共同构成了3D打印切片过程中的各种数据和配置,包括模型尺寸、挤出机配置、支撑结构、打印高度等关键信息。
FffPolygonGenerator::sliceModel接口
bool FffPolygonGenerator::sliceModel(MeshGroup* meshgroup, TimeKeeper& timeKeeper, SliceDataStorage& storage) /// slices the model
{
Progress::messageProgressStage(Progress::Stage::SLICING, &timeKeeper);
storage.model_min = meshgroup->min();
storage.model_max = meshgroup->max();
storage.model_size = storage.model_max - storage.model_min;
log("Slicing model...\n");
const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings;
// regular layers
int slice_layer_count = 0; //Use signed int because we need to subtract the initial layer in a calculation temporarily.
// Initial layer height of 0 is not allowed. Negative layer height is nonsense.
coord_t initial_layer_thickness = mesh_group_settings.get<coord_t>("layer_height_0");
if (initial_layer_thickness <= 0)
{
logError("Initial layer height %i is disallowed.\n", initial_layer_thickness);
return false;
}
// Layer height of 0 is not allowed. Negative layer height is nonsense.
const coord_t layer_thickness = mesh_group_settings.get<coord_t>("layer_height");
if(layer_thickness <= 0)
{
logError("Layer height %i is disallowed.\n", layer_thickness);
return false;
}
// variable layers
AdaptiveLayerHeights* adaptive_layer_heights = nullptr;
const bool use_variable_layer_heights = mesh_group_settings.get<bool>("adaptive_layer_height_enabled");
if (use_variable_layer_heights)
{
// Calculate adaptive layer heights
const coord_t variable_layer_height_max_variation = mesh_group_settings.get<coord_t>("adaptive_layer_height_variation");
const coord_t variable_layer_height_variation_step = mesh_group_settings.get<coord_t>("adaptive_layer_height_variation_step");
const double adaptive_threshold = mesh_group_settings.get<double>("adaptive_layer_height_threshold");
adaptive_layer_heights = new AdaptiveLayerHeights(layer_thickness, variable_layer_height_max_variation,
variable_layer_height_variation_step, adaptive_threshold);
// Get the amount of layers
slice_layer_count = adaptive_layer_heights->getLayerCount();
}
else
{
slice_layer_count = (storage.model_max.z - initial_layer_thickness) / layer_thickness + 1;
}
// Model is shallower than layer_height_0, so not even the first layer is sliced. Return an empty model then.
if (slice_layer_count <= 0)
{
return true; // This is NOT an error state!
}
std::vector<Slicer*> slicerList;
for(unsigned int mesh_idx = 0; mesh_idx < meshgroup->meshes.size(); mesh_idx++)
{
// Check if adaptive layers is populated to prevent accessing a method on NULL
std::vector<AdaptiveLayer>* adaptive_layer_height_values = {};
if (adaptive_layer_heights != nullptr)
{
adaptive_layer_height_values = adaptive_layer_heights->getLayers();
}
Mesh& mesh = meshgroup->meshes[mesh_idx];
Slicer* slicer = new Slicer(&mesh, layer_thickness, slice_layer_count, use_variable_layer_heights, adaptive_layer_height_values);
slicerList.push_back(slicer);
/*
for(SlicerLayer& layer : slicer->layers)
{
//Reporting the outline here slows down the engine quite a bit, so only do so when debugging.
sendPolygons("outline", layer_nr, layer.z, layer.polygonList);
sendPolygons("openoutline", layer_nr, layer.openPolygonList);
}
*/
Progress::messageProgress(Progress::Stage::SLICING, mesh_idx + 1, meshgroup->meshes.size());
}
// Clear the mesh face and vertex data, it is no longer needed after this point, and it saves a lot of memory.
meshgroup->clear();
Mold::process(slicerList);
Scene& scene = Application::getInstance().current_slice->scene;
for (unsigned int mesh_idx = 0; mesh_idx < slicerList.size(); mesh_idx++)
{
Mesh& mesh = scene.current_mesh_group->meshes[mesh_idx];
if (mesh.settings.get<bool>("conical_overhang_enabled") && !mesh.settings.get<bool>("anti_overhang_mesh"))
{
ConicalOverhang::apply(slicerList[mesh_idx], mesh);
}
}
MultiVolumes::carveCuttingMeshes(slicerList, scene.current_mesh_group->meshes);
Progress::messageProgressStage(Progress::Stage::PARTS, &timeKeeper);
if (scene.current_mesh_group->settings.get<bool>("carve_multiple_volumes"))
{
carveMultipleVolumes(slicerList);
}
generateMultipleVolumesOverlap(slicerList);
storage.print_layer_count = 0;
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
{
Mesh& mesh = scene.current_mesh_group->meshes[meshIdx];
Slicer* slicer = slicerList[meshIdx];
if (!mesh.settings.get<bool>("anti_overhang_mesh") && !mesh.settings.get<bool>("infill_mesh") && !mesh.settings.get<bool>("cutting_mesh"))
{
storage.print_layer_count = std::max(storage.print_layer_count, slicer->layers.size());
}
}
storage.support.supportLayers.resize(storage.print_layer_count);
storage.meshes.reserve(slicerList.size()); // causes there to be no resize in meshes so that the pointers in sliceMeshStorage._config to retraction_config don't get invalidated.
for (unsigned int meshIdx = 0; meshIdx < slicerList.size(); meshIdx++)
{
Slicer* slicer = slicerList[meshIdx];
Mesh& mesh = scene.current_mesh_group->meshes[meshIdx];
// always make a new SliceMeshStorage, so that they have the same ordering / indexing as meshgroup.meshes
storage.meshes.emplace_back(&meshgroup->meshes[meshIdx], slicer->layers.size()); // new mesh in storage had settings from the Mesh
SliceMeshStorage& meshStorage = storage.meshes.back();
// only create layer parts for normal meshes
const bool is_support_modifier = AreaSupport::handleSupportModifierMesh(storage, mesh.settings, slicer);
if (!is_support_modifier)
{
createLayerParts(meshStorage, slicer);
}
// Do not add and process support _modifier_ meshes further, and ONLY skip support _modifiers_. They have been
// processed in AreaSupport::handleSupportModifierMesh(), but other helper meshes such as infill meshes are
// processed in a later stage, except for support mesh itself, so an exception is made for that.
if (is_support_modifier && ! mesh.settings.get<bool>("support_mesh"))
{
storage.meshes.pop_back();
continue;
}
// check one if raft offset is needed
const bool has_raft = mesh_group_settings.get<EPlatformAdhesion>("adhesion_type") == EPlatformAdhesion::RAFT;
// calculate the height at which each layer is actually printed (printZ)
for (unsigned int layer_nr = 0; layer_nr < meshStorage.layers.size(); layer_nr++)
{
SliceLayer& layer = meshStorage.layers[layer_nr];
if (use_variable_layer_heights)
{
meshStorage.layers[layer_nr].printZ = adaptive_layer_heights->getLayers()->at(layer_nr).z_position;
meshStorage.layers[layer_nr].thickness = adaptive_layer_heights->getLayers()->at(layer_nr).layer_height;
}
else
{
meshStorage.layers[layer_nr].printZ = initial_layer_thickness + (layer_nr * layer_thickness);
if (layer_nr == 0)
{
meshStorage.layers[layer_nr].thickness = initial_layer_thickness;
}
else
{
meshStorage.layers[layer_nr].thickness = layer_thickness;
}
}
// add the raft offset to each layer
if (has_raft)
{
const ExtruderTrain& train = mesh_group_settings.get<ExtruderTrain&>("adhesion_extruder_nr");
layer.printZ +=
Raft::getTotalThickness()
+ train.settings.get<coord_t>("raft_airgap")
- train.settings.get<coord_t>("layer_0_z_overlap"); // shift all layers (except 0) down
if (layer_nr == 0)
{
layer.printZ += train.settings.get<coord_t>("layer_0_z_overlap"); // undo shifting down of first layer
}
}
}
delete slicerList[meshIdx];
Progress::messageProgress(Progress::Stage::PARTS, meshIdx + 1, slicerList.size());
}
return true;
}
大致流程
sliceModel
函数接收三个参数:一个MeshGroup
指针,一个TimeKeeper
引用和一个SliceDataStorage
引用。函数返回一个布尔值,表示切片操作是否成功。
1、首先计算模型的最小和最大坐标,以及模型的大小。
2、然后根据模型的设置,计算切片的层数slice_layer_count
。
3、接下来,函数遍历模型中的每个网格mesh,为每个网格创建一个Slicer
对象,并将这些Slicer
对象存储在一个列表slicerList
中。
4、处理模型的多卷切割和生成多卷重叠部分carveMultipleVolumes和generateMultipleVolumesOverlap
。
5、函数遍历每个切片器,为每个切片器创建层部分,并将它们添加到SliceMeshStorage
对象中。
6、最后,计算每个切片层的打印高度(printZ)和层厚。
首先根据是否使用可变层高,计算每个切片层的打印高度和层厚。如果使用可变层高,则从自适应层高列表中获取相应的层高信息;否则,根据初始层厚和层厚计算每个切片层的打印高度和层厚。
接下来,如果存在raft(底座),则需要将raft的偏移量添加到每个切片层上。首先获取raft的厚度、空气间隙和第0层的重叠距离等参数,然后将这些参数应用到每个切片层上,除了第0层需要特殊处理。
最后,删去切片器列表中的切片器对象,并更新进度条。整个函数返回true表示切片操作成功。
SliceMeshStorage类定义
class SliceMeshStorage
{
public:
Settings& settings;
std::vector<SliceLayer> layers;
std::string mesh_name;
LayerIndex layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content (modified while infill meshes are processed)
std::vector<AngleDegrees> infill_angles; //!< a list of angle values which is cycled through to determine the infill angle of each layer
std::vector<AngleDegrees> roofing_angles; //!< a list of angle values which is cycled through to determine the roofing angle of each layer
std::vector<AngleDegrees> skin_angles; //!< a list of angle values which is cycled through to determine the skin angle of each layer
std::vector<Polygons> overhang_areas; //!< For each layer the areas that are classified as overhang on this mesh.
std::vector<Polygons> full_overhang_areas; //!< For each layer the full overhang without the tangent of the overhang angle removed, such that the overhang area adjoins the areas of the next layers.
std::vector<std::vector<Polygons>> overhang_points; //!< For each layer a list of points where point-overhang is detected. This is overhang that hasn't got any surface area, such as a corner pointing downwards.
AABB3D bounding_box; //!< the mesh's bounding box
SubDivCube* base_subdiv_cube;
SierpinskiFillProvider* cross_fill_provider; //!< the fractal pattern for the cross (3d) filling pattern
/*!
* \brief Creates a storage space for slice results of a mesh.
* \param mesh The mesh that the storage space belongs to.
* \param slice_layer_count How many layers are needed to store the slice
* results of the mesh. This needs to be at least as high as the highest
* layer that contains a part of the mesh.
*/
SliceMeshStorage(Mesh* mesh, const size_t slice_layer_count);
virtual ~SliceMeshStorage();