629 linhas
28 KiB
C++
629 linhas
28 KiB
C++
//Copyright (C) 2013 David Braam
|
|
//Copyright (c) 2016 Ultimaker B.V.
|
|
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
|
|
|
#include <cmath> // sqrt
|
|
#include <utility> // pair
|
|
#include <deque>
|
|
#include <cmath> // round
|
|
|
|
#include "support.h"
|
|
|
|
#include "utils/math.h"
|
|
#include "progress/Progress.h"
|
|
|
|
namespace cura
|
|
{
|
|
|
|
|
|
Polygons AreaSupport::join(Polygons& supportLayer_up, Polygons& supportLayer_this, int64_t supportJoinDistance, int64_t smoothing_distance, int max_smoothing_angle, bool conical_support, int64_t conical_support_offset, int64_t conical_smallest_breadth)
|
|
{
|
|
Polygons joined;
|
|
if (conical_support)
|
|
{
|
|
Polygons insetted = supportLayer_up.offset(-conical_smallest_breadth/2);
|
|
Polygons small_parts = supportLayer_up.difference(insetted.offset(conical_smallest_breadth/2+20));
|
|
joined = supportLayer_this.unionPolygons(supportLayer_up.offset(conical_support_offset))
|
|
.unionPolygons(small_parts);
|
|
}
|
|
else
|
|
{
|
|
joined = supportLayer_this.unionPolygons(supportLayer_up);
|
|
}
|
|
// join different parts
|
|
if (supportJoinDistance > 0)
|
|
{
|
|
joined = joined.offset(supportJoinDistance)
|
|
.offset(-supportJoinDistance);
|
|
}
|
|
|
|
// remove jagged line pieces introduced by unioning separate overhang areas for consectuive layers
|
|
//
|
|
// support may otherwise look like:
|
|
// _____________________ .
|
|
// / \ } dist_from_lower_layer
|
|
// /__ __\ /
|
|
// /''--...........--''\ `\ .
|
|
// / \ } dist_from_lower_layer
|
|
// /__ __\ ./
|
|
// /''--...........--''\ `\ .
|
|
// / \ } dist_from_lower_layer
|
|
// /_______________________\ ,/
|
|
// rather than
|
|
// _____________________
|
|
// / \ .
|
|
// / \ .
|
|
// | |
|
|
// | |
|
|
// | |
|
|
// | |
|
|
// | |
|
|
// |_______________________|
|
|
//
|
|
// dist_from_lower_layer may be up to max_dist_from_lower_layer (see below), but that value may be extremely high
|
|
joined = joined.smooth_outward(max_smoothing_angle, smoothing_distance);
|
|
|
|
return joined;
|
|
}
|
|
|
|
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int layer_count)
|
|
{
|
|
int max_layer_nr_support_mesh_filled;
|
|
for (max_layer_nr_support_mesh_filled = storage.support.supportLayers.size() - 1; max_layer_nr_support_mesh_filled >= 0; max_layer_nr_support_mesh_filled--)
|
|
{
|
|
const SupportLayer& support_layer = storage.support.supportLayers[max_layer_nr_support_mesh_filled];
|
|
if (support_layer.supportAreas.size() > 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, max_layer_nr_support_mesh_filled);
|
|
for (int layer_nr = 0; layer_nr < max_layer_nr_support_mesh_filled; layer_nr++)
|
|
{
|
|
SupportLayer& support_layer = storage.support.supportLayers[max_layer_nr_support_mesh_filled];
|
|
support_layer.support_mesh = support_layer.support_mesh.unionPolygons();
|
|
}
|
|
|
|
// initialization of supportAreasPerLayer
|
|
if (layer_count > storage.support.supportLayers.size())
|
|
{ // there might alsready be anti_overhang_area data in the supportLayers
|
|
storage.support.supportLayers.resize(layer_count);
|
|
}
|
|
|
|
// generate support areas
|
|
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
|
{
|
|
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
|
if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
|
|
{
|
|
continue;
|
|
}
|
|
std::vector<Polygons> supportAreas;
|
|
supportAreas.resize(layer_count, Polygons());
|
|
generateSupportAreas(storage, mesh_idx, layer_count, supportAreas);
|
|
|
|
for (unsigned int layer_idx = 0; layer_idx < layer_count; layer_idx++)
|
|
{
|
|
storage.support.supportLayers[layer_idx].supportAreas.add(supportAreas[layer_idx]);
|
|
}
|
|
}
|
|
|
|
for (unsigned int layer_idx = 0; layer_idx < layer_count ; layer_idx++)
|
|
{
|
|
Polygons& support_areas = storage.support.supportLayers[layer_idx].supportAreas;
|
|
support_areas = support_areas.unionPolygons();
|
|
}
|
|
|
|
// handle support interface
|
|
for (unsigned int mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++)
|
|
{
|
|
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
|
if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (mesh.getSettingBoolean("support_interface_enable"))
|
|
{
|
|
generateSupportInterface(storage, mesh, layer_count);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Algorithm:
|
|
* From top layer to bottom layer:
|
|
* - find overhang by looking at the difference between two consucutive layers
|
|
* - join with support areas from layer above
|
|
* - subtract current layer
|
|
* - use the result for the next lower support layer (without doing XY-distance and Z bottom distance, so that a single support beam may move around the model a bit => more stability)
|
|
* - perform inset using X/Y-distance and bottom Z distance
|
|
*
|
|
* for support buildplate only: purge all support not connected to buildplate
|
|
*/
|
|
void AreaSupport::generateSupportAreas(SliceDataStorage& storage, unsigned int mesh_idx, unsigned int layer_count, std::vector<Polygons>& supportAreas)
|
|
{
|
|
SliceMeshStorage& mesh = storage.meshes[mesh_idx];
|
|
|
|
// given settings
|
|
ESupportType support_type = storage.getSettingAsSupportType("support_type");
|
|
|
|
if (!mesh.getSettingBoolean("support_enable"))
|
|
return;
|
|
if (support_type == ESupportType::NONE)
|
|
return;
|
|
|
|
const double supportAngle = mesh.getSettingInAngleRadians("support_angle");
|
|
const bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
|
|
const int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
|
|
const int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
|
|
const int join_distance = mesh.getSettingInMicrons("support_join_distance");
|
|
const int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height");
|
|
|
|
const int extension_offset = mesh.getSettingInMicrons("support_offset");
|
|
|
|
const int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
|
|
const int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
|
|
const double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
|
|
|
|
const int layerThickness = storage.getSettingInMicrons("layer_height");
|
|
const int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance");
|
|
const int support_xy_distance_overhang = mesh.getSettingInMicrons("support_xy_distance_overhang");
|
|
|
|
const bool use_support_xy_distance_overhang = mesh.getSettingAsSupportDistPriority("support_xy_overrides_z") == SupportDistPriority::Z_OVERRIDES_XY; // whether to use a different xy distance at overhangs
|
|
|
|
const double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
|
|
const bool conical_support = mesh.getSettingBoolean("support_conical_enabled") && conical_support_angle != 0;
|
|
const int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
|
|
|
|
int support_skin_extruder_nr = storage.getSettingAsIndex("support_interface_extruder_nr");
|
|
int support_infill_extruder_nr = storage.getSettingAsIndex("support_infill_extruder_nr");
|
|
bool interface_enable = mesh.getSettingBoolean("support_interface_enable");
|
|
|
|
// derived settings:
|
|
const int max_smoothing_angle = 135; // maximum angle of inner corners to be smoothed
|
|
int smoothing_distance;
|
|
{ // compute best smoothing_distance
|
|
ExtruderTrain& infill_train = *storage.meshgroup->getExtruderTrain(support_infill_extruder_nr);
|
|
int support_infill_line_width = infill_train.getSettingInMicrons("support_interface_line_width");
|
|
smoothing_distance = support_infill_line_width;
|
|
if (interface_enable)
|
|
{
|
|
ExtruderTrain& interface_train = *storage.meshgroup->getExtruderTrain(support_skin_extruder_nr);
|
|
int support_interface_line_width = interface_train.getSettingInMicrons("support_interface_line_width");
|
|
smoothing_distance = std::max(support_interface_line_width, smoothing_distance);
|
|
}
|
|
}
|
|
|
|
const int z_layer_distance_tower = 1; // start tower directly below overhang point
|
|
|
|
|
|
int supportLayerThickness = layerThickness;
|
|
|
|
const unsigned int layerZdistanceTop = std::max(0U, round_up_divide(supportZDistanceTop, supportLayerThickness)) + 1; // support must always be 1 layer below overhang
|
|
const unsigned int layerZdistanceBottom = std::max(0U, round_up_divide(supportZDistanceBottom, supportLayerThickness));
|
|
|
|
double tanAngle = tan(supportAngle) - 0.01; // the XY-component of the supportAngle
|
|
int max_dist_from_lower_layer = tanAngle * supportLayerThickness; // max dist which can be bridged
|
|
|
|
int64_t conical_support_offset;
|
|
if (conical_support_angle > 0)
|
|
{ // outward ==> wider base than overhang
|
|
conical_support_offset = -(tan(conical_support_angle) - 0.01) * supportLayerThickness;
|
|
}
|
|
else
|
|
{ // inward ==> smaller base than overhang
|
|
conical_support_offset = (tan(-conical_support_angle) - 0.01) * supportLayerThickness;
|
|
}
|
|
|
|
unsigned int support_layer_count = layer_count;
|
|
|
|
double tanTowerRoofAngle = tan(supportTowerRoofAngle);
|
|
int towerRoofExpansionDistance = layerThickness / tanTowerRoofAngle;
|
|
|
|
|
|
// early out
|
|
|
|
if ( layerZdistanceTop + 1 > support_layer_count )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
// computation
|
|
|
|
|
|
std::vector<std::pair<int, std::vector<Polygons>>> overhang_points; // stores overhang_points along with the layer index at which the overhang point occurs
|
|
AreaSupport::detectOverhangPoints(storage, mesh, overhang_points, layer_count, supportMinAreaSqrt);
|
|
|
|
std::deque<std::pair<Polygons, Polygons>> basic_and_full_overhang_above;
|
|
for (unsigned int layer_idx = support_layer_count - 1; layer_idx != support_layer_count - 1 - layerZdistanceTop ; layer_idx--)
|
|
{
|
|
basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer));
|
|
}
|
|
|
|
int overhang_points_pos = overhang_points.size() - 1;
|
|
Polygons supportLayer_last;
|
|
std::vector<Polygons> towerRoofs;
|
|
|
|
for (unsigned int layer_idx = support_layer_count - 1 - layerZdistanceTop; layer_idx != (unsigned int) -1 ; layer_idx--)
|
|
{
|
|
basic_and_full_overhang_above.push_front(computeBasicAndFullOverhang(storage, mesh, layer_idx, max_dist_from_lower_layer));
|
|
|
|
Polygons overhang;
|
|
{
|
|
// compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
|
|
overhang = basic_and_full_overhang_above.back().second;
|
|
basic_and_full_overhang_above.pop_back();
|
|
}
|
|
|
|
Polygons& supportLayer_this = overhang;
|
|
|
|
if (extension_offset)
|
|
{
|
|
supportLayer_this = supportLayer_this.offset(extension_offset);
|
|
}
|
|
|
|
if (supportMinAreaSqrt > 0)
|
|
{
|
|
// handle straight walls
|
|
AreaSupport::handleWallStruts(supportLayer_this, supportMinAreaSqrt, supportTowerDiameter);
|
|
// handle towers
|
|
AreaSupport::handleTowers(supportLayer_this, towerRoofs, overhang_points, overhang_points_pos, layer_idx, towerRoofExpansionDistance, supportTowerDiameter, supportMinAreaSqrt, layer_count, z_layer_distance_tower);
|
|
}
|
|
|
|
if (layer_idx+1 < support_layer_count)
|
|
{ // join with support from layer up
|
|
supportLayer_this = AreaSupport::join(supportLayer_last, supportLayer_this, join_distance, smoothing_distance, max_smoothing_angle, conical_support, conical_support_offset, conical_smallest_breadth);
|
|
}
|
|
|
|
supportLayer_this = supportLayer_this.unionPolygons(storage.support.supportLayers[layer_idx].support_mesh);
|
|
|
|
// move up from model
|
|
if (layerZdistanceBottom > 0 && layer_idx >= layerZdistanceBottom)
|
|
{
|
|
int stepHeight = support_bottom_stair_step_height / supportLayerThickness + 1;
|
|
int bottomLayer = ((layer_idx - layerZdistanceBottom) / stepHeight) * stepHeight;
|
|
supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(bottomLayer, false));
|
|
}
|
|
|
|
|
|
supportLayer_last = supportLayer_this;
|
|
|
|
|
|
// inset using X/Y distance
|
|
if (supportLayer_this.size() > 0)
|
|
{
|
|
Polygons& basic_overhang = basic_and_full_overhang_above.front().first; // basic overhang on this layer
|
|
Polygons outlines = storage.getLayerOutlines(layer_idx, false);
|
|
|
|
if (use_support_xy_distance_overhang)
|
|
{
|
|
Polygons xy_overhang_disallowed = basic_overhang.offset(supportZDistanceTop * tanAngle);
|
|
Polygons xy_non_overhang_disallowed = outlines.difference(basic_overhang.offset(supportXYDistance)).offset(supportXYDistance);
|
|
|
|
Polygons xy_disallowed = xy_overhang_disallowed.unionPolygons(xy_non_overhang_disallowed.unionPolygons(outlines.offset(support_xy_distance_overhang)));
|
|
supportLayer_this = supportLayer_this.difference(xy_disallowed);
|
|
}
|
|
else
|
|
{
|
|
supportLayer_this = supportLayer_this.difference(outlines.offset(supportXYDistance));
|
|
}
|
|
}
|
|
|
|
supportAreas[layer_idx] = supportLayer_this;
|
|
|
|
Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size());
|
|
}
|
|
|
|
// do stuff for when support on buildplate only
|
|
if (supportOnBuildplateOnly)
|
|
{
|
|
Polygons touching_buildplate = supportAreas[0]; // TODO: not working for conical support!
|
|
for (unsigned int layer_idx = 1 ; layer_idx < storage.support.supportLayers.size() ; layer_idx++)
|
|
{
|
|
Polygons& supportLayer = supportAreas[layer_idx];
|
|
|
|
if (conical_support)
|
|
{ // with conical support the next layer is allowed to be larger than the previous
|
|
touching_buildplate = touching_buildplate.offset(std::abs(conical_support_offset) + 10, ClipperLib::jtMiter, 10);
|
|
// + 10 and larger miter limit cause performing an outward offset after an inward offset can disregard sharp corners
|
|
//
|
|
// conical support can make
|
|
// layer above layer below
|
|
// v v
|
|
// | : |
|
|
// | ==> : |__
|
|
// |____ :....
|
|
//
|
|
// a miter limit would result in
|
|
// | : : |
|
|
// | :.. <== : |__
|
|
// .\___ :....
|
|
//
|
|
|
|
}
|
|
|
|
touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
|
|
|
|
supportAreas[layer_idx] = touching_buildplate;
|
|
}
|
|
}
|
|
|
|
//Enforce top Z distance.
|
|
if (layerZdistanceTop > 1)
|
|
{
|
|
// this is performed after the main support generation loop above, because it affects the joining of polygons
|
|
// if this would be performed in the main loop then some support would not have been generated under the overhangs and consequently no support is generated for that,
|
|
// meaning almost no support would be generated in some cases which definitely need support.
|
|
for (size_t layer_idx = 0; layer_idx < storage.support.supportLayers.size() && layer_idx < support_layer_count - (layerZdistanceTop - 1); layer_idx++)
|
|
{
|
|
supportAreas[layer_idx] = supportAreas[layer_idx].difference(storage.getLayerOutlines(layer_idx + layerZdistanceTop - 1, false));
|
|
}
|
|
}
|
|
|
|
for (unsigned int layer_idx = supportAreas.size() - 1; layer_idx != (unsigned int) std::max(-1, storage.support.layer_nr_max_filled_layer) ; layer_idx--)
|
|
{
|
|
const Polygons& support_here = supportAreas[layer_idx];
|
|
if (support_here.size() > 0)
|
|
{
|
|
storage.support.layer_nr_max_filled_layer = layer_idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
storage.support.generated = true;
|
|
}
|
|
|
|
|
|
/* layer 2
|
|
* layer 1 ______________|
|
|
* _______| ^^^^^ basic overhang
|
|
*
|
|
* ^^^^^^^ supporter
|
|
* ^^^^^^^^^^^^^^^^^ supported
|
|
* ^^^^^^^^^^^^^^^^^^^^^^ supportee
|
|
* ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended
|
|
* ^^^^^^^^^ overhang extensions
|
|
* ^^^^^^^^^^^^^^ overhang
|
|
*/
|
|
std::pair<Polygons, Polygons> AreaSupport::computeBasicAndFullOverhang(const SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_idx, const int64_t max_dist_from_lower_layer)
|
|
{
|
|
Polygons supportLayer_supportee = mesh.layers[layer_idx].getOutlines();
|
|
Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1, false);
|
|
|
|
Polygons supportLayer_supported = supportLayer_supporter.offset(max_dist_from_lower_layer);
|
|
Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
|
|
|
|
const SupportLayer& support_layer = storage.support.supportLayers[layer_idx];
|
|
if (support_layer.anti_overhang.size())
|
|
{
|
|
basic_overhang = basic_overhang.difference(support_layer.anti_overhang);
|
|
}
|
|
|
|
// Polygons support_extension = basic_overhang.offset(max_dist_from_lower_layer);
|
|
// support_extension = support_extension.intersection(supportLayer_supported);
|
|
// support_extension = support_extension.intersection(supportLayer_supportee);
|
|
//
|
|
// Polygons overhang = basic_overhang.unionPolygons(support_extension);
|
|
// presumably the computation above is slower than the one below
|
|
|
|
Polygons overhang_extented = basic_overhang.offset(max_dist_from_lower_layer + 100); // +100 for easier joining with support from layer above
|
|
Polygons full_overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee));
|
|
return std::make_pair(basic_overhang, full_overhang);
|
|
}
|
|
|
|
|
|
void AreaSupport::detectOverhangPoints(
|
|
SliceDataStorage& storage,
|
|
SliceMeshStorage& mesh,
|
|
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points, // stores overhang_points along with the layer index at which the overhang point occurs)
|
|
int layer_count,
|
|
int supportMinAreaSqrt
|
|
)
|
|
{
|
|
ExtruderTrain* infill_extr = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_infill_extruder_nr"));
|
|
const unsigned int support_line_width = infill_extr->getSettingInMicrons("support_line_width");
|
|
for (int layer_idx = 0; layer_idx < layer_count; layer_idx++)
|
|
{
|
|
SliceLayer& layer = mesh.layers[layer_idx];
|
|
for (SliceLayerPart& part : layer.parts)
|
|
{
|
|
if (part.outline.outerPolygon().area() < supportMinAreaSqrt * supportMinAreaSqrt)
|
|
{
|
|
Polygons part_poly_computed;
|
|
Polygons& part_poly = (part.insets.size() > 0) ? part.insets[0] : part_poly_computed; // don't copy inset if its already computed
|
|
if (part.insets.size() == 0)
|
|
{
|
|
part_poly_computed = part.outline.offset(-support_line_width / 2);
|
|
}
|
|
|
|
if (part_poly.size() > 0)
|
|
{
|
|
Polygons part_poly_recomputed = part_poly.difference(storage.support.supportLayers[layer_idx].anti_overhang);
|
|
if (part_poly_recomputed.size() == 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (overhang_points.size() > 0 && overhang_points.back().first == layer_idx)
|
|
overhang_points.back().second.push_back(part_poly_recomputed);
|
|
else
|
|
{
|
|
std::vector<Polygons> small_part_polys;
|
|
small_part_polys.push_back(part_poly_recomputed);
|
|
overhang_points.emplace_back<std::pair<int, std::vector<Polygons>>>(std::make_pair(layer_idx, small_part_polys));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void AreaSupport::handleTowers(
|
|
Polygons& supportLayer_this,
|
|
std::vector<Polygons>& towerRoofs,
|
|
std::vector<std::pair<int, std::vector<Polygons>>>& overhang_points,
|
|
int& overhang_points_pos,
|
|
int layer_idx,
|
|
int towerRoofExpansionDistance,
|
|
int supportTowerDiameter,
|
|
int supportMinAreaSqrt,
|
|
int layer_count,
|
|
int z_layer_distance_tower
|
|
)
|
|
{
|
|
// handle new tower roof tops
|
|
int layer_overhang_point = layer_idx + z_layer_distance_tower;
|
|
if (overhang_points_pos >= 0 && layer_overhang_point < layer_count &&
|
|
overhang_points[overhang_points_pos].first == layer_overhang_point)
|
|
{
|
|
std::vector<Polygons>& overhang_points_here = overhang_points[overhang_points_pos].second;
|
|
{ // make sure we have the lowest point (make polys empty if they have small parts below)
|
|
if (overhang_points_pos > 0 && overhang_points[overhang_points_pos - 1].first == layer_overhang_point - 1)
|
|
{
|
|
std::vector<Polygons>& overhang_points_below = overhang_points[overhang_points_pos - 1].second;
|
|
for (Polygons& poly_here : overhang_points_here)
|
|
{
|
|
for (Polygons& poly_below : overhang_points_below)
|
|
{
|
|
poly_here = poly_here.difference(poly_below.offset(supportMinAreaSqrt*2));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (Polygons& poly : overhang_points_here)
|
|
if (poly.size() > 0)
|
|
towerRoofs.push_back(poly);
|
|
overhang_points_pos--;
|
|
}
|
|
|
|
// make tower roofs
|
|
for (unsigned int roof_idx = 0; roof_idx < towerRoofs.size(); roof_idx++)
|
|
{
|
|
Polygons& tower_roof = towerRoofs[roof_idx];
|
|
if (tower_roof.size() > 0)
|
|
{
|
|
supportLayer_this = supportLayer_this.unionPolygons(tower_roof);
|
|
|
|
if (tower_roof[0].area() < supportTowerDiameter * supportTowerDiameter)
|
|
{
|
|
tower_roof = tower_roof.offset(towerRoofExpansionDistance);
|
|
}
|
|
else
|
|
{
|
|
tower_roof.clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AreaSupport::handleWallStruts(
|
|
Polygons& supportLayer_this,
|
|
int supportMinAreaSqrt,
|
|
int supportTowerDiameter
|
|
)
|
|
{
|
|
for (unsigned int p = 0; p < supportLayer_this.size(); p++)
|
|
{
|
|
PolygonRef poly = supportLayer_this[p];
|
|
if (poly.size() < 6) // might be a single wall
|
|
{
|
|
PolygonRef poly = supportLayer_this[p];
|
|
int best = -1;
|
|
int best_length2 = -1;
|
|
for (unsigned int i = 0; i < poly.size(); i++)
|
|
{
|
|
int length2 = vSize2(poly[i] - poly[(i+1) % poly.size()]);
|
|
if (length2 > best_length2)
|
|
{
|
|
best = i;
|
|
best_length2 = length2;
|
|
}
|
|
}
|
|
|
|
if (best_length2 < supportMinAreaSqrt * supportMinAreaSqrt)
|
|
break; // this is a small area, not a wall!
|
|
|
|
|
|
// an estimate of the width of the area
|
|
int width = sqrt( poly.area() * poly.area() / best_length2 ); // sqrt (a^2 / l^2) instead of a / sqrt(l^2)
|
|
|
|
// add square tower (strut) in the middle of the wall
|
|
if (width < supportMinAreaSqrt)
|
|
{
|
|
Point mid = (poly[best] + poly[(best+1) % poly.size()] ) / 2;
|
|
Polygons struts;
|
|
PolygonRef strut = struts.newPoly();
|
|
strut.add(mid + Point( supportTowerDiameter/2, supportTowerDiameter/2));
|
|
strut.add(mid + Point(-supportTowerDiameter/2, supportTowerDiameter/2));
|
|
strut.add(mid + Point(-supportTowerDiameter/2, -supportTowerDiameter/2));
|
|
strut.add(mid + Point( supportTowerDiameter/2, -supportTowerDiameter/2));
|
|
supportLayer_this = supportLayer_this.unionPolygons(struts);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, const unsigned int layer_count)
|
|
{
|
|
const unsigned int roof_layer_count = round_divide(mesh.getSettingInMicrons("support_roof_height"), storage.getSettingInMicrons("layer_height"));
|
|
const unsigned int bottom_layer_count = round_divide(mesh.getSettingInMicrons("support_bottom_height"), storage.getSettingInMicrons("layer_height"));
|
|
const unsigned int z_distance_bottom = round_up_divide(mesh.getSettingInMicrons("support_bottom_distance"), storage.getSettingInMicrons("layer_height"));
|
|
const unsigned int z_distance_top = round_up_divide(mesh.getSettingInMicrons("support_top_distance"), storage.getSettingInMicrons("layer_height"));
|
|
|
|
const int skip_layer_count = std::max(1u, round_divide(mesh.getSettingInMicrons("support_interface_skip_height"), storage.getSettingInMicrons("layer_height")));
|
|
const int interface_line_width = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_interface_extruder_nr"))->getSettingInMicrons("support_interface_line_width");
|
|
|
|
std::vector<SupportLayer>& supportLayers = storage.support.supportLayers;
|
|
for (unsigned int layer_idx = 0; layer_idx < layer_count; layer_idx++)
|
|
{
|
|
SupportLayer& layer = supportLayers[layer_idx];
|
|
|
|
const unsigned int top_layer_idx_above = layer_idx + roof_layer_count + z_distance_top;
|
|
const unsigned int bottom_layer_idx_below = std::max(0, int(layer_idx) - int(bottom_layer_count) - int(z_distance_bottom));
|
|
if (top_layer_idx_above >= supportLayers.size())
|
|
{
|
|
continue;
|
|
}
|
|
Polygons roofs;
|
|
if (roof_layer_count > 0)
|
|
{
|
|
Polygons model;
|
|
const unsigned int n_scans = std::max(1u, (roof_layer_count - 1) / skip_layer_count);
|
|
const float z_skip = std::max(1.0f, float(roof_layer_count - 1) / float(n_scans));
|
|
for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip)
|
|
{
|
|
const Polygons outlines_above = mesh.layers[std::round(layer_idx_above)].getOutlines();
|
|
model = model.unionPolygons(outlines_above);
|
|
}
|
|
roofs = layer.supportAreas.intersection(model);
|
|
}
|
|
Polygons bottoms;
|
|
if (bottom_layer_count > 0)
|
|
{
|
|
Polygons model;
|
|
const unsigned int n_scans = std::max(1u, (bottom_layer_count - 1) / skip_layer_count);
|
|
const float z_skip = std::max(1.0f, float(bottom_layer_count - 1) / float(n_scans));
|
|
for (float layer_idx_below = bottom_layer_idx_below; std::round(layer_idx_below) < (int)(layer_idx - z_distance_bottom); layer_idx_below += z_skip)
|
|
{
|
|
const Polygons outlines_below = mesh.layers[std::round(layer_idx_below)].getOutlines();
|
|
model = model.unionPolygons(outlines_below);
|
|
}
|
|
bottoms = layer.supportAreas.intersection(model);
|
|
}
|
|
// expand skin a bit so that we're sure it's not too thin to be printed.
|
|
Polygons skin = roofs.unionPolygons(bottoms).offset(interface_line_width).intersection(layer.supportAreas);
|
|
skin.removeSmallAreas(1.0);
|
|
layer.skin.add(skin);
|
|
layer.supportAreas = layer.supportAreas.difference(layer.skin);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}//namespace cura
|