Example #1
0
Point PrimeTower::getLocationBeforePrimeTower(const SliceDataStorage& storage) const
{
    Point ret(0, 0);
    int absolute_starting_points = 0;
    for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
    {
        ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(0);
        if (train.getSettingBoolean("machine_extruder_start_pos_abs"))
        {
            ret += Point(train.getSettingInMicrons("machine_extruder_start_pos_x"), train.getSettingInMicrons("machine_extruder_start_pos_y"));
            absolute_starting_points++;
        }
    }
    if (absolute_starting_points > 0)
    { // take the average over all absolute starting positions
        ret /= absolute_starting_points;
    }
    else
    { // use the middle of the bed
        if (!storage.getSettingBoolean("machine_center_is_zero"))
        {
            ret = Point(storage.getSettingInMicrons("machine_width"), storage.getSettingInMicrons("machine_depth")) / 2;
        }
        // otherwise keep (0, 0)
    }
    return ret;
}
Example #2
0
void PrimeTower::generateGroundpoly(const SliceDataStorage& storage)
{
    extruder_count = storage.meshgroup->getExtruderCount();

    int64_t prime_tower_wall_thickness = storage.getSettingInMicrons("prime_tower_wall_thickness");
    int64_t tower_size = storage.getSettingInMicrons("prime_tower_size");

    if (prime_tower_wall_thickness * 2 < tower_size)
    {
        is_hollow = true;
    }

    PolygonRef p = ground_poly.newPoly();
    int tower_distance = 0; 
    int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
    int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
    p.add(Point(x + tower_distance, y + tower_distance));
    p.add(Point(x + tower_distance, y + tower_distance + tower_size));
    p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
    p.add(Point(x + tower_distance - tower_size, y + tower_distance));
    middle = Point(x - tower_size / 2, y + tower_size / 2);

    if (is_hollow)
    {
        ground_poly = ground_poly.difference(ground_poly.offset(-prime_tower_wall_thickness));
    }

    post_wipe_point = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
}
Example #3
0
int Raft::getFillerLayerHeight(const SliceDataStorage& storage)
{
    if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
    {
        const int64_t normal_layer_height = storage.getSettingInMicrons("layer_height");
        return normal_layer_height;
    }
    const unsigned int filler_layer_height = round_divide(getZdiffBetweenRaftAndLayer1(storage), getFillerLayerCount(storage));
    return filler_layer_height;
}
Example #4
0
void PrimeTower::computePrimeTowerMax(SliceDataStorage& storage)
{   // compute storage.max_object_height_second_to_last_extruder, which is used to determine the highest point in the prime tower

    extruder_count = storage.getSettingAsCount("machine_extruder_count");

    int max_object_height_per_extruder[extruder_count];
    {   // compute max_object_height_per_extruder
        memset(max_object_height_per_extruder, -1, sizeof(max_object_height_per_extruder));
        for (SliceMeshStorage& mesh : storage.meshes)
        {
            max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")] =
                std::max(   max_object_height_per_extruder[mesh.getSettingAsIndex("extruder_nr")]
                            ,   mesh.layer_nr_max_filled_layer  );
        }
        int support_extruder_nr = storage.getSettingAsIndex("support_extruder_nr"); // TODO: support extruder should be configurable per object
        max_object_height_per_extruder[support_extruder_nr] =
            std::max(   max_object_height_per_extruder[support_extruder_nr]
                        ,   storage.support.layer_nr_max_filled_layer  );
        int support_roof_extruder_nr = storage.getSettingAsIndex("support_roof_extruder_nr"); // TODO: support roof extruder should be configurable per object
        max_object_height_per_extruder[support_roof_extruder_nr] =
            std::max(   max_object_height_per_extruder[support_roof_extruder_nr]
                        ,   storage.support.layer_nr_max_filled_layer  );
    }
    {   // // compute max_object_height_second_to_last_extruder
        int extruder_max_object_height = 0;
        for (int extruder_nr = 1; extruder_nr < extruder_count; extruder_nr++)
        {
            if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_max_object_height])
            {
                extruder_max_object_height = extruder_nr;
            }
        }
        int extruder_second_max_object_height = -1;
        for (int extruder_nr = 0; extruder_nr < extruder_count; extruder_nr++)
        {
            if (extruder_nr == extruder_max_object_height) {
                continue;
            }
            if (max_object_height_per_extruder[extruder_nr] > max_object_height_per_extruder[extruder_second_max_object_height])
            {
                extruder_second_max_object_height = extruder_nr;
            }
        }
        if (extruder_second_max_object_height < 0)
        {
            storage.max_object_height_second_to_last_extruder = -1;
        }
        else
        {
            storage.max_object_height_second_to_last_extruder = max_object_height_per_extruder[extruder_second_max_object_height];
        }
    }

}
Example #5
0
PrimeTower::PrimeTower(const SliceDataStorage& storage)
: is_hollow(false)
, wipe_from_middle(false)
{
    enabled = storage.getSettingBoolean("prime_tower_enable")
           && storage.getSettingInMicrons("prime_tower_wall_thickness") > 10
           && storage.getSettingInMicrons("prime_tower_size") > 10;
    if (enabled)
    {
        generateGroundpoly(storage);
    }
}
Example #6
0
void PrimeTower::generateGroundpoly(SliceDataStorage& storage) 
{
    PolygonRef p = storage.primeTower.ground_poly.newPoly();
    int tower_size = storage.getSettingInMicrons("prime_tower_size");
    int tower_distance = 0; 
    int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
    int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
    p.add(Point(x + tower_distance, y + tower_distance));
    p.add(Point(x + tower_distance, y + tower_distance + tower_size));
    p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
    p.add(Point(x + tower_distance - tower_size, y + tower_distance));

    storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);   
}
Example #7
0
int Raft::getZdiffBetweenRaftAndLayer1(const SliceDataStorage& storage)
{
    const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"));
    if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
    {
        return 0;
    }
    const int64_t airgap = std::max((coord_t)0, train.getSettingInMicrons("raft_airgap"));
    const int64_t layer_0_overlap = storage.getSettingInMicrons("layer_0_z_overlap");

    const int64_t layer_height_0 = storage.getSettingInMicrons("layer_height_0");

    const int64_t z_diff_raft_to_bottom_of_layer_1 = std::max(int64_t(0), airgap + layer_height_0 - layer_0_overlap);
    return z_diff_raft_to_bottom_of_layer_1;
}
Example #8
0
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
{
    if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable"))
    {
        generatePaths3(storage);
    }
}
void FffPolygonGenerator::processOozeShield(SliceDataStorage& storage, unsigned int total_layers)
{
    if (!getSettingBoolean("ooze_shield_enabled"))
    {
        return;
    }
    
    int ooze_shield_dist = getSettingInMicrons("ooze_shield_dist");
    
    for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
    {
        storage.oozeShield.push_back(storage.getLayerOutlines(layer_nr, true).offset(ooze_shield_dist));
    }
    
    int largest_printed_radius = MM2INT(1.0); // TODO: make var a parameter, and perhaps even a setting?
    for(unsigned int layer_nr=0; layer_nr<total_layers; layer_nr++)
    {
        storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].offset(-largest_printed_radius).offset(largest_printed_radius); 
    }
    int allowed_angle_offset = tan(getSettingInAngleRadians("ooze_shield_angle")) * getSettingInMicrons("layer_height");//Allow for a 60deg angle in the oozeShield.
    for(unsigned int layer_nr=1; layer_nr<total_layers; layer_nr++)
    {
        storage.oozeShield[layer_nr] = storage.oozeShield[layer_nr].unionPolygons(storage.oozeShield[layer_nr-1].offset(-allowed_angle_offset));
    }
    for(unsigned int layer_nr=total_layers-1; layer_nr>0; layer_nr--)
    {
        storage.oozeShield[layer_nr-1] = storage.oozeShield[layer_nr-1].unionPolygons(storage.oozeShield[layer_nr].offset(-allowed_angle_offset));
    }
}
Example #10
0
Comb::Comb(SliceDataStorage& storage, int layer_nr, Polygons& comb_boundary_inside, int64_t comb_boundary_offset, bool travel_avoid_other_parts, int64_t travel_avoid_distance)
: storage(storage)
, layer_nr(layer_nr)
, offset_from_outlines(comb_boundary_offset) // between second wall and infill / other walls
, max_moveInside_distance2(offset_from_outlines * 2 * offset_from_outlines * 2)
, offset_from_outlines_outside(travel_avoid_distance)
, offset_from_inside_to_outside(offset_from_outlines + offset_from_outlines_outside)
, max_crossing_dist2(offset_from_inside_to_outside * offset_from_inside_to_outside * 2) // so max_crossing_dist = offset_from_inside_to_outside * sqrt(2) =approx 1.5 to allow for slightly diagonal crossings and slightly inaccurate crossing computation
, avoid_other_parts(travel_avoid_other_parts)
, boundary_inside( comb_boundary_inside )
, partsView_inside( boundary_inside.splitIntoPartsView() ) // WARNING !! changes the order of boundary_inside !!
, inside_loc_to_line(PolygonUtils::createLocToLineGrid(boundary_inside, comb_boundary_offset))
, boundary_outside(
        [&storage, layer_nr, travel_avoid_distance]()
        {
            return storage.getLayerOutlines(layer_nr, false).offset(travel_avoid_distance);
        }
    )
, outside_loc_to_line(
        [](Comb* comber, const int64_t offset_from_inside_to_outside)
        {
            return PolygonUtils::createLocToLineGrid(comber->getBoundaryOutside(), offset_from_inside_to_outside * 3 / 2);
        }
        , this
        , offset_from_inside_to_outside
    )
{
}
Example #11
0
int Raft::getTotalThickness(const SliceDataStorage& storage)
{
    const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"));
    return train.getSettingInMicrons("raft_base_thickness")
        + train.getSettingInMicrons("raft_interface_thickness")
        + train.getSettingAsCount("raft_surface_layers") * train.getSettingInMicrons("raft_surface_thickness");
}
Example #12
0
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, 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())
        {
            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 = support_areas[layer_idx].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 = support_areas[layer_idx].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(support_areas[layer_idx]);
            skin.removeSmallAreas(1.0);
            layer.skin.add(skin);
            layer.supportAreas.add(support_areas[layer_idx].difference(layer.skin));
        }
        else 
        {
            layer.skin.add(support_areas[layer_idx]);
        }
    }
}
Example #13
0
int Raft::getTotalExtraLayers(const SliceDataStorage& storage)
{
    const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"));
    if (train.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
    {
        return 0;
    }
    return 2 + train.getSettingAsCount("raft_surface_layers") + getFillerLayerCount(storage);
}
Example #14
0
void PrimeTower::generatePaths(SliceDataStorage& storage, unsigned int total_layers)
{
    if (storage.max_object_height_second_to_last_extruder >= 0
//         && storage.getSettingInMicrons("prime_tower_distance") > 0
            && storage.getSettingInMicrons("prime_tower_size") > 0)
    {
        generatePaths3(storage);
    }
}
Example #15
0
void PrimeTower::generatePaths_OLD(SliceDataStorage& storage, unsigned int total_layers)
{
    
    if (storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingBoolean("prime_tower_enable"))
    {
        PolygonRef p = storage.primeTower.ground_poly.newPoly();
        int tower_size = storage.getSettingInMicrons("prime_tower_size");
        int tower_distance = 0; 
        int x = storage.getSettingInMicrons("prime_tower_position_x"); // storage.model_max.x
        int y = storage.getSettingInMicrons("prime_tower_position_y"); // storage.model_max.y
        p.add(Point(x + tower_distance, y + tower_distance));
        p.add(Point(x + tower_distance, y + tower_distance + tower_size));
        p.add(Point(x + tower_distance - tower_size, y + tower_distance + tower_size));
        p.add(Point(x + tower_distance - tower_size, y + tower_distance));

        storage.wipePoint = Point(x + tower_distance - tower_size / 2, y + tower_distance + tower_size / 2);
    }
}
Example #16
0
void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const unsigned int primary_line_count, const int primary_extruder_skirt_brim_line_width, const bool is_skirt, const bool outside_only, Polygons& first_layer_outline)
{
    bool external_only = is_skirt; // whether to include holes or not
    const int layer_nr = 0;
    if (is_skirt)
    {
        const bool include_helper_parts = true;
        first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
        first_layer_outline = first_layer_outline.approxConvexHull();
    }
    else
    { // add brim underneath support by removing support where there's brim around the model
        const bool include_helper_parts = false; // include manually below
        first_layer_outline = storage.getLayerOutlines(layer_nr, include_helper_parts, external_only);
        first_layer_outline.add(storage.primeTower.ground_poly); // don't remove parts of the prime tower, but make a brim for it
        if (outside_only)
        {
            first_layer_outline = first_layer_outline.removeEmptyHoles();
        }
        if (storage.support.generated && primary_line_count > 0)
        { // remove model-brim from support
            // avoid gap in the middle
            //    V
            //  +---+     +----+
            //  |+-+|     |+--+|
            //  || ||     ||[]|| > expand to fit an extra brim line
            //  |+-+|     |+--+|
            //  +---+     +----+ 
            const Polygons& model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2)); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides
            SupportLayer& support_layer = storage.support.supportLayers[0];
            support_layer.supportAreas = support_layer.supportAreas.difference(model_brim_covered_area);
            first_layer_outline.add(support_layer.supportAreas);
            first_layer_outline.add(support_layer.skin);
        }
    }
    constexpr int join_distance = 20;
    first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon
    constexpr int smallest_line_length = 200;
    constexpr int largest_error_of_removed_point = 50;
    first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines
}
Example #17
0
void Raft::generate(SliceDataStorage& storage, int distance)
{
    assert(storage.raftOutline.size() == 0 && "Raft polygon isn't generated yet, so should be empty!");
    storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound);
    ExtruderTrain* train = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("adhesion_extruder_nr"));
    const int shield_line_width_layer0 = train->getSettingInMicrons("skirt_brim_line_width");
    if (storage.draft_protection_shield.size() > 0)
    {
        Polygons draft_shield_raft = storage.draft_protection_shield.offset(shield_line_width_layer0) // start half a line width outside shield
                                        .difference(storage.draft_protection_shield.offset(-distance - shield_line_width_layer0 / 2, ClipperLib::jtRound)); // end distance inside shield
        storage.raftOutline = storage.raftOutline.unionPolygons(draft_shield_raft);
    }
    if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
    {
        const Polygons& ooze_shield = storage.oozeShield[0];
        Polygons ooze_shield_raft = ooze_shield.offset(shield_line_width_layer0) // start half a line width outside shield
                                        .difference(ooze_shield.offset(-distance - shield_line_width_layer0 / 2, ClipperLib::jtRound)); // end distance inside shield
        storage.raftOutline = storage.raftOutline.unionPolygons(ooze_shield_raft);
    }
    coord_t smoothing = train->getSettingInMicrons("raft_smoothing");
    storage.raftOutline = storage.raftOutline.offset(smoothing, ClipperLib::jtRound).offset(-smoothing, ClipperLib::jtRound); // remove small holes and smooth inward corners
}
Example #18
0
void generateRaft(SliceDataStorage& storage, int distance)
{
    if (storage.draft_protection_shield.size() > 0)
    {
        storage.raftOutline = storage.raftOutline.unionPolygons(storage.draft_protection_shield.offset(distance, ClipperLib::jtRound));
    }
    else if (storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0)
    {
        storage.raftOutline = storage.raftOutline.unionPolygons(storage.oozeShield[0].offset(distance, ClipperLib::jtRound));
    }
    else 
    {
        storage.raftOutline = storage.getLayerOutlines(0, true).offset(distance, ClipperLib::jtRound);
    }
}
Example #19
0
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));
                    }
                }
                
            }
        }
    }
}
Example #20
0
void PrimeTower::generatePaths_denseInfill(const SliceDataStorage& storage)
{
    int n_patterns = 2; // alternating patterns between layers
    int infill_overlap = 60; // so that it can't be zero; EDIT: wtf?
    int extra_infill_shift = 0;

    int64_t z = 0; // (TODO) because the prime tower stores the paths for each extruder for once instead of generating each layer, we don't know the z position

    for (int extruder = 0; extruder < extruder_count; extruder++)
    {
        int line_width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("prime_tower_line_width");
        patterns_per_extruder.emplace_back(n_patterns);
        std::vector<ExtrusionMoves>& patterns = patterns_per_extruder.back();
        patterns.resize(n_patterns);
        for (int pattern_idx = 0; pattern_idx < n_patterns; pattern_idx++)
        {
            patterns[pattern_idx].polygons = ground_poly.offset(-line_width / 2);
            Polygons& result_lines = patterns[pattern_idx].lines;
            int outline_offset = -line_width;
            int line_distance = line_width;
            double fill_angle = 45 + pattern_idx * 90;
            Polygons result_polygons; // should remain empty, since we generate lines pattern!
            constexpr bool zig_zaggify_infill = false;
            Infill infill_comp(EFillMethod::LINES, zig_zaggify_infill, ground_poly, outline_offset, line_width, line_distance, infill_overlap, fill_angle, z, extra_infill_shift);
            infill_comp.generate(result_polygons, result_lines);
        }
        int line_width_layer0 = line_width;
        if (storage.getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::RAFT)
        {
            line_width_layer0 *= storage.meshgroup->getExtruderTrain(extruder)->getSettingAsRatio("initial_layer_line_width_factor");
        }
        pattern_per_extruder_layer0.emplace_back();
        ExtrusionMoves& pattern = pattern_per_extruder_layer0.back();
        pattern.polygons = ground_poly.offset(-line_width_layer0 / 2);
        int outline_offset = -line_width_layer0;
        int line_distance = line_width_layer0;
        double fill_angle = 45;
        Polygons result_polygons;
        constexpr bool zig_zaggify_infill = false;
        Infill infill_comp(EFillMethod::LINES, zig_zaggify_infill, ground_poly, outline_offset, line_width_layer0, line_distance, infill_overlap, fill_angle, z, extra_infill_shift);
        infill_comp.generate(result_polygons, pattern.lines);
    }
}
Example #21
0
void PrimeTower::addToGcode(SliceDataStorage& storage, GCodePlanner& gcodeLayer, GCodeExport& gcode, int layer_nr, int prev_extruder, bool prime_tower_dir_outward, bool wipe, int* last_prime_tower_poly_printed)
{
    if (!( storage.max_object_height_second_to_last_extruder >= 0 && storage.getSettingInMicrons("prime_tower_size") > 0) )
    {
        return;
    }
    bool prime_tower_added = false;
    for (int extruder = 0; extruder <  storage.meshgroup->getExtruderCount() && !prime_tower_added; extruder++)
    {
        prime_tower_added = last_prime_tower_poly_printed[extruder] == int(layer_nr);
    }
    if (prime_tower_added)
    { // don't print the prime tower if it has been printed already
        return;
    }
    
    if (prev_extruder == gcodeLayer.getExtruder())
    {
        wipe = false;
    }
    addToGcode3(storage, gcodeLayer, gcode, layer_nr, prev_extruder, prime_tower_dir_outward, wipe, last_prime_tower_poly_printed);
}
Example #22
0
/*            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];
    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 FffPolygonGenerator::processDraftShield(SliceDataStorage& storage, unsigned int total_layers)
{
    int draft_shield_height = getSettingInMicrons("draft_shield_height");
    int draft_shield_dist = getSettingInMicrons("draft_shield_dist");
    int layer_height_0 = getSettingInMicrons("layer_height_0");
    int layer_height = getSettingInMicrons("layer_height");
    
    if (draft_shield_height < layer_height_0)
    {
        return;
    }
    
    unsigned int max_screen_layer = (draft_shield_height - layer_height_0) / layer_height + 1;
    
    int layer_skip = 500 / layer_height + 1;
    
    Polygons& draft_shield = storage.draft_protection_shield;
    for (unsigned int layer_nr = 0; layer_nr < total_layers && layer_nr < max_screen_layer; layer_nr += layer_skip)
    {
        draft_shield = draft_shield.unionPolygons(storage.getLayerOutlines(layer_nr, true));
    }
    
    storage.draft_protection_shield = draft_shield.convexHull(draft_shield_dist);
}
Example #24
0
void SkirtBrim::generate(SliceDataStorage& storage, int start_distance, unsigned int primary_line_count)
{
    const bool is_skirt = start_distance > 0;

    const unsigned int adhesion_extruder_nr = storage.getSettingAsIndex("adhesion_extruder_nr");
    const ExtruderTrain* adhesion_extruder = storage.meshgroup->getExtruderTrain(adhesion_extruder_nr);
    const int primary_extruder_skirt_brim_line_width = adhesion_extruder->getSettingInMicrons("skirt_brim_line_width") * adhesion_extruder->getSettingAsRatio("initial_layer_line_width_factor");
    const int64_t primary_extruder_minimal_length = adhesion_extruder->getSettingInMicrons("skirt_brim_minimal_length");

    Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr];

    Polygons first_layer_outline;
    getFirstLayerOutline(storage, primary_line_count, primary_extruder_skirt_brim_line_width, is_skirt, first_layer_outline);

    const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0;
    const bool has_draft_shield = storage.draft_protection_shield.size() > 0;

    if (is_skirt && (has_ooze_shield || has_draft_shield))
    { // make sure we don't generate skirt through draft / ooze shield
        first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield);
        if (has_ooze_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
        }
        first_layer_outline = first_layer_outline.approxConvexHull();
        start_distance = primary_extruder_skirt_brim_line_width / 2;
    }

    int offset_distance = generatePrimarySkirtBrimLines(start_distance, primary_line_count, primary_extruder_skirt_brim_line_width, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder);


    // generate brim for ooze shield and draft shield
    if (!is_skirt && (has_ooze_shield || has_draft_shield))
    {
        // generate areas where to make extra brim for the shields
        // avoid gap in the middle
        //    V
        //  +---+     +----+
        //  |+-+|     |+--+|
        //  || ||     ||[]|| > expand to fit an extra brim line
        //  |+-+|     |+--+|
        //  +---+     +----+ 
        const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides

        Polygons shield_brim;
        if (has_ooze_shield)
        {
            shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width));
        }
        if (has_draft_shield)
        {
            shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)));
        }
        const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);
        shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width));

        // generate brim within shield_brim
        skirt_brim_primary_extruder.add(shield_brim);
        while (shield_brim.size() > 0)
        {
            shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width);
            skirt_brim_primary_extruder.add(shield_brim);
        }

        // update parameters to generate secondary skirt around
        first_layer_outline = outer_primary_brim;
        if (has_draft_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield);
        }
        if (has_ooze_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
        }

        offset_distance = 0;
    }

    { // process other extruders' brim/skirt (as one brim line around the old brim)
        int last_width = primary_extruder_skirt_brim_line_width;
        std::vector<bool> extruder_is_used = storage.getExtrudersUsed();
        for (unsigned int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
        {
            if (extruder == adhesion_extruder_nr || !extruder_is_used[extruder])
            {
                continue;
            }
            const ExtruderTrain* train = storage.meshgroup->getExtruderTrain(extruder);
            const int width = train->getSettingInMicrons("skirt_brim_line_width") * train->getSettingAsRatio("initial_layer_line_width_factor");
            const int64_t minimal_length = train->getSettingInMicrons("skirt_brim_minimal_length");
            offset_distance += last_width / 2 + width/2;
            last_width = width;
            while (storage.skirt_brim[extruder].polygonLength() < minimal_length)
            {
                storage.skirt_brim[extruder].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound));
                offset_distance += width;
            }
        }
    }
}
Example #25
0
void SkirtBrim::generate(SliceDataStorage& storage, Polygons first_layer_outline, int start_distance, unsigned int primary_line_count)
{
    const bool is_skirt = start_distance > 0;

    Scene& scene = Application::getInstance().current_slice->scene;
    const size_t adhesion_extruder_nr = scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr").extruder_nr;
    const Settings& adhesion_settings = scene.extruders[adhesion_extruder_nr].settings;
    const coord_t primary_extruder_skirt_brim_line_width = adhesion_settings.get<coord_t>("skirt_brim_line_width") * adhesion_settings.get<Ratio>("initial_layer_line_width_factor");
    const coord_t primary_extruder_minimal_length = adhesion_settings.get<coord_t>("skirt_brim_minimal_length");

    Polygons& skirt_brim_primary_extruder = storage.skirt_brim[adhesion_extruder_nr];

    const bool has_ooze_shield = storage.oozeShield.size() > 0 && storage.oozeShield[0].size() > 0;
    const bool has_draft_shield = storage.draft_protection_shield.size() > 0;

    if (is_skirt && (has_ooze_shield || has_draft_shield))
    { // make sure we don't generate skirt through draft / ooze shield
        first_layer_outline = first_layer_outline.offset(start_distance - primary_extruder_skirt_brim_line_width / 2, ClipperLib::jtRound).unionPolygons(storage.draft_protection_shield);
        if (has_ooze_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
        }
        first_layer_outline = first_layer_outline.approxConvexHull();
        start_distance = primary_extruder_skirt_brim_line_width / 2;
    }

    int offset_distance = generatePrimarySkirtBrimLines(start_distance, primary_line_count, primary_extruder_minimal_length, first_layer_outline, skirt_brim_primary_extruder);

    // handle support-brim
    const ExtruderTrain& support_infill_extruder = scene.current_mesh_group->settings.get<ExtruderTrain&>("support_infill_extruder_nr");
    if (support_infill_extruder.settings.get<bool>("support_brim_enable"))
    {
        generateSupportBrim(storage);
    }

    // generate brim for ooze shield and draft shield
    if (!is_skirt && (has_ooze_shield || has_draft_shield))
    {
        // generate areas where to make extra brim for the shields
        // avoid gap in the middle
        //    V
        //  +---+     +----+
        //  |+-+|     |+--+|
        //  || ||     ||[]|| > expand to fit an extra brim line
        //  |+-+|     |+--+|
        //  +---+     +----+ 
        const int64_t primary_skirt_brim_width = (primary_line_count + primary_line_count % 2) * primary_extruder_skirt_brim_line_width; // always use an even number, because we will fil the area from both sides

        Polygons shield_brim;
        if (has_ooze_shield)
        {
            shield_brim = storage.oozeShield[0].difference(storage.oozeShield[0].offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width));
        }
        if (has_draft_shield)
        {
            shield_brim = shield_brim.unionPolygons(storage.draft_protection_shield.difference(storage.draft_protection_shield.offset(-primary_skirt_brim_width - primary_extruder_skirt_brim_line_width)));
        }
        const Polygons outer_primary_brim = first_layer_outline.offset(offset_distance, ClipperLib::jtRound);
        shield_brim = shield_brim.difference(outer_primary_brim.offset(primary_extruder_skirt_brim_line_width));

        // generate brim within shield_brim
        skirt_brim_primary_extruder.add(shield_brim);
        while (shield_brim.size() > 0)
        {
            shield_brim = shield_brim.offset(-primary_extruder_skirt_brim_line_width);
            skirt_brim_primary_extruder.add(shield_brim);
        }

        // update parameters to generate secondary skirt around
        first_layer_outline = outer_primary_brim;
        if (has_draft_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.draft_protection_shield);
        }
        if (has_ooze_shield)
        {
            first_layer_outline = first_layer_outline.unionPolygons(storage.oozeShield[0]);
        }

        offset_distance = 0;
    }

    { // process other extruders' brim/skirt (as one brim line around the old brim)
        int last_width = primary_extruder_skirt_brim_line_width;
        std::vector<bool> extruder_is_used = storage.getExtrudersUsed();
        for (size_t extruder_nr = 0; extruder_nr < Application::getInstance().current_slice->scene.extruders.size(); extruder_nr++)
        {
            if (extruder_nr == adhesion_extruder_nr || !extruder_is_used[extruder_nr])
            {
                continue;
            }
            const ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders[extruder_nr];
            const coord_t width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor");
            const coord_t minimal_length = train.settings.get<coord_t>("skirt_brim_minimal_length");
            offset_distance += last_width / 2 + width/2;
            last_width = width;
            while (storage.skirt_brim[extruder_nr].polygonLength() < minimal_length)
            {
                storage.skirt_brim[extruder_nr].add(first_layer_outline.offset(offset_distance, ClipperLib::jtRound));
                offset_distance += width;
            }
        }
    }
}
Example #26
0
void SkirtBrim::getFirstLayerOutline(SliceDataStorage& storage, const size_t primary_line_count, const bool is_skirt, Polygons& first_layer_outline)
{
    const ExtruderTrain& train = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("adhesion_extruder_nr");
    const ExtruderTrain& support_infill_extruder = Application::getInstance().current_slice->scene.current_mesh_group->settings.get<ExtruderTrain&>("support_infill_extruder_nr");
    const bool external_only = is_skirt || train.settings.get<bool>("brim_outside_only"); //Whether to include holes or not. Skirt doesn't have any holes.
    const LayerIndex layer_nr = 0;
    if (is_skirt)
    {
        constexpr bool include_support = true;
        constexpr bool include_prime_tower = true;
        first_layer_outline = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, external_only);
        first_layer_outline = first_layer_outline.approxConvexHull();
    }
    else
    { // add brim underneath support by removing support where there's brim around the model
        constexpr bool include_support = false; //Include manually below.
        constexpr bool include_prime_tower = false; //Include manually below.
        constexpr bool external_outlines_only = false; //Remove manually below.
        first_layer_outline = storage.getLayerOutlines(layer_nr, include_support, include_prime_tower, external_outlines_only);
        first_layer_outline = first_layer_outline.unionPolygons(); //To guard against overlapping outlines, which would produce holes according to the even-odd rule.
        Polygons first_layer_empty_holes;
        if (external_only)
        {
            first_layer_empty_holes = first_layer_outline.getEmptyHoles();
            first_layer_outline = first_layer_outline.removeEmptyHoles();
        }
        if (storage.support.generated && primary_line_count > 0 && !storage.support.supportLayers.empty())
        { // remove model-brim from support
            SupportLayer& support_layer = storage.support.supportLayers[0];
            if (support_infill_extruder.settings.get<bool>("brim_replaces_support"))
            {
                // avoid gap in the middle
                //    V
                //  +---+     +----+
                //  |+-+|     |+--+|
                //  || ||     ||[]|| > expand to fit an extra brim line
                //  |+-+|     |+--+|
                //  +---+     +----+
                const coord_t primary_extruder_skirt_brim_line_width = train.settings.get<coord_t>("skirt_brim_line_width") * train.settings.get<Ratio>("initial_layer_line_width_factor");
                Polygons model_brim_covered_area = first_layer_outline.offset(primary_extruder_skirt_brim_line_width * (primary_line_count + primary_line_count % 2), ClipperLib::jtRound); // always leave a gap of an even number of brim lines, so that it fits if it's generating brim from both sides
                if (external_only)
                { // don't remove support within empty holes where no brim is generated.
                    model_brim_covered_area.add(first_layer_empty_holes);
                }
                AABB model_brim_covered_area_boundary_box(model_brim_covered_area);
                support_layer.excludeAreasFromSupportInfillAreas(model_brim_covered_area, model_brim_covered_area_boundary_box);
            }
            for (const SupportInfillPart& support_infill_part : support_layer.support_infill_parts)
            {
                first_layer_outline.add(support_infill_part.outline);
            }
            first_layer_outline.add(support_layer.support_bottom);
            first_layer_outline.add(support_layer.support_roof);
        }
        if (storage.primeTower.enabled)
        {
            first_layer_outline.add(storage.primeTower.outer_poly_first_layer); // don't remove parts of the prime tower, but make a brim for it
        }
    }
    constexpr coord_t join_distance = 20;
    first_layer_outline = first_layer_outline.offset(join_distance).offset(-join_distance); // merge adjacent models into single polygon
    constexpr coord_t smallest_line_length = 200;
    constexpr coord_t largest_error_of_removed_point = 50;
    first_layer_outline.simplify(smallest_line_length, largest_error_of_removed_point); // simplify for faster processing of the brim lines
    if (first_layer_outline.size() == 0)
    {
        logError("Couldn't generate skirt / brim! No polygons on first layer.\n");
    }
}
Example #27
0
/* 
 * 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, CommandSocket* commandSocket)
{
    SliceMeshStorage& mesh = storage.meshes[mesh_idx];
        
    // given settings
    ESupportType support_type = mesh.getSettingAsSupportType("support_type");
    
    if (!mesh.getSettingBoolean("support_enable"))
        return;
    if (support_type == ESupportType::NONE)
        return;
    
    double supportAngle = mesh.getSettingInAngleRadians("support_angle");
    bool supportOnBuildplateOnly = support_type == ESupportType::PLATFORM_ONLY;
    int supportZDistance = mesh.getSettingInMicrons("support_z_distance");
    int supportZDistanceBottom = mesh.getSettingInMicrons("support_bottom_distance");
    int supportZDistanceTop = mesh.getSettingInMicrons("support_top_distance");
    int join_distance = mesh.getSettingInMicrons("support_join_distance");
    int support_bottom_stair_step_height = mesh.getSettingInMicrons("support_bottom_stair_step_height");
    int smoothing_distance = mesh.getSettingInMicrons("support_area_smoothing"); 
    
    int extension_offset = mesh.getSettingInMicrons("support_offset");
    
    int supportTowerDiameter = mesh.getSettingInMicrons("support_tower_diameter");
    int supportMinAreaSqrt = mesh.getSettingInMicrons("support_minimal_diameter");
    double supportTowerRoofAngle = mesh.getSettingInAngleRadians("support_tower_roof_angle");
    
    //std::cerr <<" towerDiameter=" << towerDiameter <<", supportMinAreaSqrt=" << supportMinAreaSqrt << std::endl;
    
    int min_smoothing_area = 100*100; // minimal area for which to perform smoothing
    int z_layer_distance_tower = 1; // start tower directly below overhang point
        
    int layerThickness = mesh.getSettingInMicrons("layer_height");
    int extrusionWidth = mesh.getSettingInMicrons("support_line_width"); 
    int supportXYDistance = mesh.getSettingInMicrons("support_xy_distance") + extrusionWidth / 2;
    
    bool conical_support = mesh.getSettingBoolean("support_conical_enabled");
    double conical_support_angle = mesh.getSettingInAngleRadians("support_conical_angle");
    int64_t conical_smallest_breadth = mesh.getSettingInMicrons("support_conical_min_width");
    
    // derived settings:
    
    if (supportZDistanceBottom < 0) supportZDistanceBottom = supportZDistance;
    if (supportZDistanceTop < 0)    supportZDistanceTop = supportZDistance;
    
    
    int supportLayerThickness = layerThickness;
    
    int layerZdistanceTop       = supportZDistanceTop / supportLayerThickness + 1; // support must always be 1 layer below overhang
    unsigned int layerZdistanceBottom    = std::max(0, supportZDistanceBottom / supportLayerThickness); 

    double tanAngle = tan(supportAngle) - 0.01;  // the XY-component of the supportAngle
    int maxDistFromLowerLayer = tanAngle * supportLayerThickness; // max dist which can be bridged
    
    int64_t conical_support_offset;
    if (conical_support_angle > 0) 
    {
        conical_support_offset = (tan(conical_support_angle) - 0.01) * supportLayerThickness;
    }
    else 
    {
        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 > (int) support_layer_count )
    {
        storage.support.generated = false; // no (first layer) support can be generated 
        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, extrusionWidth);
        
    
    bool still_in_upper_empty_layers = true;
    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--)
    {
        
        Polygons overhang;
        {
            // compute basic overhang and put in right layer ([layerZdistanceTOp] layers below)
            Polygons supportLayer_supportee = mesh.layers[layer_idx+layerZdistanceTop].getOutlines();
            Polygons supportLayer_supporter = storage.getLayerOutlines(layer_idx-1+layerZdistanceTop, false);
            
            Polygons supportLayer_supported =  supportLayer_supporter.offset(maxDistFromLowerLayer);
            Polygons basic_overhang = supportLayer_supportee.difference(supportLayer_supported);
        
//         Polygons support_extension = basic_overhang.offset(maxDistFromLowerLayer);
//         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(maxDistFromLowerLayer + 100); // +100 for easier joining with support from layer above
            overhang = overhang_extented.intersection(supportLayer_supported.unionPolygons(supportLayer_supportee));
            
            /*            layer 2
            * layer 1 ______________|
            * _______|         ^^^^^ basic overhang
            * 
            * ^^^^^^^ supporter
            * ^^^^^^^^^^^^^^^^^ supported
            * ^^^^^^^^^^^^^^^^^^^^^^ supportee
            *         ^^^^^^^^^^^^^^^^^^^^^^^^ overhang extended
            *         ^^^^^^^^^      overhang extensions
            *         ^^^^^^^^^^^^^^ overhang
            */
        }

        Polygons& supportLayer_this = overhang; 
        
        if (extension_offset)
        {
            supportLayer_this = supportLayer_this.offset(extension_offset);
        }
        
        supportLayer_this.simplify(50); // TODO: hardcoded value!
        
        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, min_smoothing_area, conical_support, conical_support_offset, conical_smallest_breadth);
        }
        
        
        // 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)
            supportLayer_this = supportLayer_this.difference(storage.getLayerOutlines(layer_idx, false).offset(supportXYDistance));
        
        supportAreas[layer_idx] = supportLayer_this;
        
        if (still_in_upper_empty_layers && supportLayer_this.size() > 0)
        {
            storage.support.layer_nr_max_filled_layer = layer_idx;
            still_in_upper_empty_layers = false;
        }
        
        Progress::messageProgress(Progress::Stage::SUPPORT, storage.meshes.size() * mesh_idx + support_layer_count - layer_idx, support_layer_count * storage.meshes.size(), commandSocket);
    }
    
    // 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];
            
            touching_buildplate = supportLayer.intersection(touching_buildplate); // from bottom to top, support areas can only decrease!
            
            supportAreas[layer_idx] = touching_buildplate;
        }
    }
}
Example #28
0
/* 
 * 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));
            }
            if (storage.primeTower.enabled) //Don't intersect with prime tower.
            {
                supportLayer_this = supportLayer_this.difference(storage.primeTower.ground_poly.getOutsidePolygons());
            }
        }

        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;
        }
    }


    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;
}
void FffPolygonGenerator::slices2polygons(SliceDataStorage& storage, TimeKeeper& time_keeper)
{
    size_t total_layers = 0;
    for (SliceMeshStorage& mesh : storage.meshes)
    {
        total_layers = std::max<unsigned int>(total_layers, mesh.layers.size());
    }
    
    //layerparts2HTML(storage, "output/output.html");
    for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
    {
        processInsets(storage, layer_number);
        
        Progress::messageProgress(Progress::Stage::INSET, layer_number+1, total_layers, commandSocket);
    }
    
    removeEmptyFirstLayers(storage, getSettingInMicrons("layer_height"), total_layers);
    
    if (total_layers < 1)
    {
        log("Stopping process because there are no layers.\n");
        return;
    }
    
    Progress::messageProgressStage(Progress::Stage::SUPPORT, &time_keeper, commandSocket);  
            
    AreaSupport::generateSupportAreas(storage, total_layers, commandSocket);
    /*
    if (storage.support.generated)
    {
        for (unsigned int layer_idx = 0; layer_idx < total_layers; layer_idx++)
        {
            Polygons& support = storage.support.supportLayers[layer_idx].supportAreas;
            sendPolygons(SupportType, layer_idx, support, getSettingInMicrons("support_line_width"));
        }
    }
    */
    
    Progress::messageProgressStage(Progress::Stage::SKIN, &time_keeper, commandSocket);
    int mesh_max_bottom_layer_count = 0;
    if (getSettingBoolean("magic_spiralize"))
    {
        for(SliceMeshStorage& mesh : storage.meshes)
        {
            mesh_max_bottom_layer_count = std::max(mesh_max_bottom_layer_count, mesh.getSettingAsCount("bottom_layers"));
        }
    }
    for(unsigned int layer_number = 0; layer_number < total_layers; layer_number++)
    {
        if (!getSettingBoolean("magic_spiralize") || static_cast<int>(layer_number) < mesh_max_bottom_layer_count)    //Only generate up/downskin and infill for the first X layers when spiralize is choosen.
        {
            processSkins(storage, layer_number);
        }
        Progress::messageProgress(Progress::Stage::SKIN, layer_number+1, total_layers, commandSocket);
    }
    
    unsigned int combined_infill_layers = storage.getSettingInMicrons("infill_sparse_thickness") / std::max(storage.getSettingInMicrons("layer_height"),1); //How many infill layers to combine to obtain the requested sparse thickness.
    for(SliceMeshStorage& mesh : storage.meshes)
    {
        combineInfillLayers(mesh,combined_infill_layers);
    }

    storage.primeTower.computePrimeTowerMax(storage);
    storage.primeTower.generatePaths(storage, total_layers);
    
    processOozeShield(storage, total_layers);
        
    processDraftShield(storage, total_layers);
    
    processPlatformAdhesion(storage);

    
    for(SliceMeshStorage& mesh : storage.meshes)
    {
        if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled"))
        {
            processFuzzyWalls(mesh);
        }
        else if (mesh.getSettingAsCount("wall_line_count") > 0)
        { // only send polygon data
            for (unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
            {
                SliceLayer* layer = &mesh.layers[layer_nr];
                for(SliceLayerPart& part : layer->parts)
                {
                    sendPolygons(Inset0Type, layer_nr, (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0], mesh.getSettingInMicrons("wall_line_width_0"));
                }
            }
        }
    }
}
Example #30
0
int Raft::getFillerLayerCount(const SliceDataStorage& storage)
{
    const int64_t normal_layer_height = storage.getSettingInMicrons("layer_height");
    const unsigned int filler_layer_count = round_divide(getZdiffBetweenRaftAndLayer1(storage), normal_layer_height);
    return filler_layer_count;
}