void generateConcentricInfillDense(Polygons outline, Polygons& result, Polygons* in_between, int extrusionWidth, bool avoidOverlappingPerimeters)
{
    while(outline.size() > 0)
    {
        for (unsigned int polyNr = 0; polyNr < outline.size(); polyNr++)
        {
            PolygonRef r = outline[polyNr];
            result.add(r);
        }
        Polygons next_outline;
        PolygonUtils::offsetExtrusionWidth(outline, true, extrusionWidth, next_outline, in_between, avoidOverlappingPerimeters);
        outline = next_outline;
    } 

}
Exemple #2
0
void Weaver::weave(PrintObject* object, CommandSocket* commandSocket)
{
    int maxz = object->max().z;

    int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1;

    DEBUG_SHOW(layer_count);

    std::vector<cura::Slicer*> slicerList;

    for(Mesh& mesh : object->meshes)
    {
        cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
        slicerList.push_back(slicer);
    }

    
    int starting_layer_idx;
    { // find first non-empty layer
        for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++)
        {
            Polygons parts;
            for (cura::Slicer* slicer : slicerList)
                parts.add(slicer->layers[starting_layer_idx].polygonList);  
            
            if (parts.size() > 0)
                break;
        }
        if (starting_layer_idx > 0)
        {
            logError("First %i layers are empty!\n", starting_layer_idx);
        }
    }
    
    
    std::cerr<< "chainifying layers..." << std::endl;
    {
        int starting_z = -1;
        for (cura::Slicer* slicer : slicerList)
            wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygonList);
        
        if (commandSocket)
            commandSocket->sendPolygons(Inset0Type, 0, wireFrame.bottom_outline);
        
        wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
        
        Point starting_point_in_layer;
        if (wireFrame.bottom_outline.size() > 0)
            starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2;
        else 
            starting_point_in_layer = (Point(0,0) + object->max() + object->min()) / 2;
        
        for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
        {
            logProgress("inset", layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
            
            Polygons parts1;
            for (cura::Slicer* slicer : slicerList)
                parts1.add(slicer->layers[layer_idx].polygonList);

            
            Polygons chainified;

            chainify_polygons(parts1, starting_point_in_layer, chainified, false);
            
            if (commandSocket)
                commandSocket->sendPolygons(Inset0Type, layer_idx - starting_layer_idx, chainified);
            
            if (chainified.size() > 0)
            {
                if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
                wireFrame.layers.emplace_back();
                WeaveLayer& layer = wireFrame.layers.back();
                
                layer.z0 = slicerList[0]->layers[layer_idx-1].z - starting_z;
                layer.z1 = slicerList[0]->layers[layer_idx].z - starting_z;
                
                layer.supported = chainified;
                
                starting_point_in_layer = layer.supported.back().back();
            }
        }
    }
    
    std::cerr<< "finding horizontal parts..." << std::endl;
    {
        Polygons* lower_top_parts = &wireFrame.bottom_outline;
        
        for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
        {
            logProgress("skin", layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
            
            WeaveLayer& layer = wireFrame.layers[layer_idx];
            
            Polygons empty;
            Polygons& layer_above = (layer_idx+1 < wireFrame.layers.size())? wireFrame.layers[layer_idx+1].supported : empty;
            
            createHorizontalFill(*lower_top_parts, layer, layer_above, layer.z1);
            lower_top_parts = &layer.supported;
        }
    }
    // at this point layer.supported still only contains the polygons to be connected
    // when connecting layers, we further add the supporting polygons created by the roofs
    
    std::cerr<< "connecting layers..." << std::endl;
    {
        Polygons* lower_top_parts = &wireFrame.bottom_outline;
        int last_z = wireFrame.z_bottom;
        for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) // use top of every layer but the last
        {
            WeaveLayer& layer = wireFrame.layers[layer_idx];
            
            connect_polygons(*lower_top_parts, last_z, layer.supported, layer.z1, layer);
            layer.supported.add(layer.roofs.roof_outlines);
            lower_top_parts = &layer.supported;
            
            last_z = layer.z1;
        }
    }


    { // roofs:
        
        WeaveLayer& top_layer = wireFrame.layers.back();
        Polygons to_be_supported; // empty for the top layer
        fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
    }
    
    
    { // bottom:
        Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
        fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
    }
    
}
Exemple #3
0
void Weaver::weave(MeshGroup* meshgroup)
{   
    wireFrame.meshgroup = meshgroup;
    
    const coord_t maxz = meshgroup->max().z;

    const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings;
    const coord_t initial_layer_thickness = mesh_group_settings.get<coord_t>("layer_height_0");
    const coord_t connection_height = mesh_group_settings.get<coord_t>("wireframe_height");
    const size_t layer_count = (maxz - initial_layer_thickness) / connection_height + 1;
    std::vector<AdaptiveLayer> layer_thicknesses;

    log("Layer count: %i\n", layer_count);

    std::vector<cura::Slicer*> slicerList;

    for(Mesh& mesh : meshgroup->meshes)
    {
        constexpr bool variable_layer_heights = false;
        cura::Slicer* slicer = new cura::Slicer(&mesh, connection_height, layer_count, variable_layer_heights, &layer_thicknesses);
        slicerList.push_back(slicer);
    }

    LayerIndex starting_layer_idx;
    { // find first non-empty layer
        for (starting_layer_idx = 0; starting_layer_idx < LayerIndex(layer_count); starting_layer_idx++)
        {
            Polygons parts;
            for (cura::Slicer* slicer : slicerList)
                parts.add(slicer->layers[starting_layer_idx].polygons);  
            
            if (parts.size() > 0)
                break;
        }
        if (starting_layer_idx > 0)
        {
            logWarning("First %i layers are empty!\n", starting_layer_idx);
        }
    }

    log("Chainifying layers...\n");
    {
        int starting_z = -1;
        for (cura::Slicer* slicer : slicerList)
            wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygons);

        Application::getInstance().communication->sendPolygons(PrintFeatureType::OuterWall, wireFrame.bottom_outline, 1, 1, 1);
        
        if (slicerList.empty()) //Wait, there is nothing to slice.
        {
            wireFrame.z_bottom = 0;
        }
        else
        {
            wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
        }
        
        Point starting_point_in_layer;
        if (wireFrame.bottom_outline.size() > 0)
        {
            starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2;
        }
        else
        {
            starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
        }
        
        Progress::messageProgressStage(Progress::Stage::INSET_SKIN, nullptr);
        for (LayerIndex layer_idx = starting_layer_idx + 1; layer_idx < LayerIndex(layer_count); layer_idx++)
        {
            Progress::messageProgress(Progress::Stage::INSET_SKIN, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
            
            Polygons parts1;
            for (cura::Slicer* slicer : slicerList)
                parts1.add(slicer->layers[layer_idx].polygons);

            
            Polygons chainified;

            chainify_polygons(parts1, starting_point_in_layer, chainified);

            Application::getInstance().communication->sendPolygons(PrintFeatureType::OuterWall, chainified, 1, 1, 1);

            if (chainified.size() > 0)
            {
                if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
                wireFrame.layers.emplace_back();
                WeaveLayer& layer = wireFrame.layers.back();
                
                layer.z0 = slicerList[0]->layers[layer_idx-1].z - starting_z;
                layer.z1 = slicerList[0]->layers[layer_idx].z - starting_z;
                
                layer.supported = chainified;
                
                starting_point_in_layer = layer.supported.back().back();
            }
        }
    }

    log("Finding horizontal parts...\n");
    {
        Progress::messageProgressStage(Progress::Stage::SUPPORT, nullptr);
        for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
        {
            Progress::messageProgress(Progress::Stage::SUPPORT, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
            
            WeaveLayer& layer = wireFrame.layers[layer_idx];
            
            Polygons empty;
            Polygons& layer_above = (layer_idx+1 < wireFrame.layers.size())? wireFrame.layers[layer_idx+1].supported : empty;
            
            createHorizontalFill(layer, layer_above);
        }
    }
    // at this point layer.supported still only contains the polygons to be connected
    // when connecting layers, we further add the supporting polygons created by the roofs

    log("Connecting layers...\n");
    {
        Polygons* lower_top_parts = &wireFrame.bottom_outline;
        int last_z = wireFrame.z_bottom;
        for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) // use top of every layer but the last
        {
            WeaveLayer& layer = wireFrame.layers[layer_idx];
            
            connect_polygons(*lower_top_parts, last_z, layer.supported, layer.z1, layer);
            layer.supported.add(layer.roofs.roof_outlines);
            lower_top_parts = &layer.supported;
            
            last_z = layer.z1;
        }
    }


    { // roofs:
        if (!wireFrame.layers.empty()) //If there are no layers, create no roof.
        {
            WeaveLayer& top_layer = wireFrame.layers.back();
            Polygons to_be_supported; // empty for the top layer
            fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
        }
    }
    
    
    { // bottom:
        if (!wireFrame.layers.empty()) //If there are no layers, create no bottom.
        {
            Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
            fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
        }
    }
    
}
Exemple #4
0
void SlicerLayer::makePolygons(Mesh* mesh, bool keep_none_closed, bool extensive_stitching)
{
    Polygons openPolygonList;
    
    for(unsigned int startSegment=0; startSegment < segmentList.size(); startSegment++)
    {
        if (segmentList[startSegment].addedToPolygon)
            continue;
        
        Polygon poly;
        poly.add(segmentList[startSegment].start);
        
        unsigned int segmentIndex = startSegment;
        bool canClose;
        while(true)
        {
            canClose = false;
            segmentList[segmentIndex].addedToPolygon = true;
            Point p0 = segmentList[segmentIndex].end;
            poly.add(p0);
            int nextIndex = -1;
            const MeshFace& face = mesh->faces[segmentList[segmentIndex].faceIndex];
            for(unsigned int i=0;i<3;i++)
            {
                decltype(face_idx_to_segment_index.begin()) it;
                if (face.connected_face_index[i] > -1 && (it = face_idx_to_segment_index.find(face.connected_face_index[i])) != face_idx_to_segment_index.end())
                {
                    int index = (*it).second;
                    Point p1 = segmentList[index].start;
                    Point diff = p0 - p1;
                    if (shorterThen(diff, MM2INT(0.01)))
                    {
                        if (index == static_cast<int>(startSegment))
                            canClose = true;
                        if (segmentList[index].addedToPolygon)
                            continue;
                        nextIndex = index;
                    }
                }
            }
            if (nextIndex == -1)
                break;
            segmentIndex = nextIndex;
        }
        if (canClose)
            polygonList.add(poly);
        else
            openPolygonList.add(poly);
    }
    //Clear the segmentList to save memory, it is no longer needed after this point.
    segmentList.clear();

    //Connecting polygons that are not closed yet, as models are not always perfect manifold we need to join some stuff up to get proper polygons
    //First link up polygon ends that are within 2 microns.
    for(unsigned int i=0;i<openPolygonList.size();i++)
    {
        if (openPolygonList[i].size() < 1) continue;
        for(unsigned int j=0;j<openPolygonList.size();j++)
        {
            if (openPolygonList[j].size() < 1) continue;
            
            Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0];
            int64_t distSquared = vSize2(diff);

            if (distSquared < MM2INT(0.02) * MM2INT(0.02))
            {
                if (i == j)
                {
                    polygonList.add(openPolygonList[i]);
                    openPolygonList[i].clear();
                    break;
                }else{
                    for(unsigned int n=0; n<openPolygonList[j].size(); n++)
                        openPolygonList[i].add(openPolygonList[j][n]);

                    openPolygonList[j].clear();
                }
            }
        }
    }

    //Next link up all the missing ends, closing up the smallest gaps first. This is an inefficient implementation which can run in O(n*n*n) time.
    while(1)
    {
        int64_t bestScore = MM2INT(10.0) * MM2INT(10.0);
        unsigned int bestA = -1;
        unsigned int bestB = -1;
        bool reversed = false;
        for(unsigned int i=0;i<openPolygonList.size();i++)
        {
            if (openPolygonList[i].size() < 1) continue;
            for(unsigned int j=0;j<openPolygonList.size();j++)
            {
                if (openPolygonList[j].size() < 1) continue;
                
                Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][0];
                int64_t distSquared = vSize2(diff);
                if (distSquared < bestScore)
                {
                    bestScore = distSquared;
                    bestA = i;
                    bestB = j;
                    reversed = false;
                }

                if (i != j)
                {
                    Point diff = openPolygonList[i][openPolygonList[i].size()-1] - openPolygonList[j][openPolygonList[j].size()-1];
                    int64_t distSquared = vSize2(diff);
                    if (distSquared < bestScore)
                    {
                        bestScore = distSquared;
                        bestA = i;
                        bestB = j;
                        reversed = true;
                    }
                }
            }
        }
        
        if (bestScore >= MM2INT(10.0) * MM2INT(10.0))
            break;
        
        if (bestA == bestB)
        {
            polygonList.add(openPolygonList[bestA]);
            openPolygonList[bestA].clear();
        }else{
            if (reversed)
            {
                if (openPolygonList[bestA].polygonLength() > openPolygonList[bestB].polygonLength())
                {
                    for(unsigned int n=openPolygonList[bestB].size()-1; int(n)>=0; n--)
                        openPolygonList[bestA].add(openPolygonList[bestB][n]);
                    openPolygonList[bestB].clear();
                }else{
                    for(unsigned int n=openPolygonList[bestA].size()-1; int(n)>=0; n--)
                        openPolygonList[bestB].add(openPolygonList[bestA][n]);
                    openPolygonList[bestA].clear();
                }
            }else{
                for(unsigned int n=0; n<openPolygonList[bestB].size(); n++)
                    openPolygonList[bestA].add(openPolygonList[bestB][n]);
                openPolygonList[bestB].clear();
            }
        }
    }

    if (extensive_stitching)
    {
        //For extensive stitching find 2 open polygons that are touching 2 closed polygons.
        // Then find the sortest path over this polygon that can be used to connect the open polygons,
        // And generate a path over this shortest bit to link up the 2 open polygons.
        // (If these 2 open polygons are the same polygon, then the final result is a closed polyon)
        
        while(1)
        {
            unsigned int bestA = -1;
            unsigned int bestB = -1;
            gapCloserResult bestResult;
            bestResult.len = POINT_MAX;
            bestResult.polygonIdx = -1;
            bestResult.pointIdxA = -1;
            bestResult.pointIdxB = -1;
            
            for(unsigned int i=0; i<openPolygonList.size(); i++)
            {
                if (openPolygonList[i].size() < 1) continue;
                
                {
                    gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[i][openPolygonList[i].size()-1]);
                    if (res.len > 0 && res.len < bestResult.len)
                    {
                        bestA = i;
                        bestB = i;
                        bestResult = res;
                    }
                }

                for(unsigned int j=0; j<openPolygonList.size(); j++)
                {
                    if (openPolygonList[j].size() < 1 || i == j) continue;
                    
                    gapCloserResult res = findPolygonGapCloser(openPolygonList[i][0], openPolygonList[j][openPolygonList[j].size()-1]);
                    if (res.len > 0 && res.len < bestResult.len)
                    {
                        bestA = i;
                        bestB = j;
                        bestResult = res;
                    }
                }
            }
            
            if (bestResult.len < POINT_MAX)
            {
                if (bestA == bestB)
                {
                    if (bestResult.pointIdxA == bestResult.pointIdxB)
                    {
                        polygonList.add(openPolygonList[bestA]);
                        openPolygonList[bestA].clear();
                    }
                    else if (bestResult.AtoB)
                    {
                        PolygonRef poly = polygonList.newPoly();
                        for(unsigned int j = bestResult.pointIdxA; j != bestResult.pointIdxB; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
                            poly.add(polygonList[bestResult.polygonIdx][j]);
                        for(unsigned int j = openPolygonList[bestA].size() - 1; int(j) >= 0; j--)
                            poly.add(openPolygonList[bestA][j]);
                        openPolygonList[bestA].clear();
                    }
                    else
                    {
                        unsigned int n = polygonList.size();
                        polygonList.add(openPolygonList[bestA]);
                        for(unsigned int j = bestResult.pointIdxB; j != bestResult.pointIdxA; j = (j + 1) % polygonList[bestResult.polygonIdx].size())
                            polygonList[n].add(polygonList[bestResult.polygonIdx][j]);
                        openPolygonList[bestA].clear();
                    }
                }
                else
                {
                    if (bestResult.pointIdxA == bestResult.pointIdxB)
                    {
                        for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
                            openPolygonList[bestB].add(openPolygonList[bestA][n]);
                        openPolygonList[bestA].clear();
                    }
                    else if (bestResult.AtoB)
                    {
                        Polygon poly;
                        for(unsigned int n = bestResult.pointIdxA; n != bestResult.pointIdxB; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
                            poly.add(polygonList[bestResult.polygonIdx][n]);
                        for(unsigned int n=poly.size()-1;int(n) >= 0; n--)
                            openPolygonList[bestB].add(poly[n]);
                        for(unsigned int n=0; n<openPolygonList[bestA].size(); n++)
                            openPolygonList[bestB].add(openPolygonList[bestA][n]);
                        openPolygonList[bestA].clear();
                    }
                    else
                    {
                        for(unsigned int n = bestResult.pointIdxB; n != bestResult.pointIdxA; n = (n + 1) % polygonList[bestResult.polygonIdx].size())
                            openPolygonList[bestB].add(polygonList[bestResult.polygonIdx][n]);
                        for(unsigned int n = openPolygonList[bestA].size() - 1; int(n) >= 0; n--)
                            openPolygonList[bestB].add(openPolygonList[bestA][n]);
                        openPolygonList[bestA].clear();
                    }
                }
            }
            else
            {
                break;
            }
        }
    }

    if (keep_none_closed)
    {
        for(unsigned int n=0; n<openPolygonList.size(); n++)
        {
            if (openPolygonList[n].size() > 0)
                polygonList.add(openPolygonList[n]);
        }
    }
    for(unsigned int i=0;i<openPolygonList.size();i++)
    {
        if (openPolygonList[i].size() > 0)
            openPolygons.newPoly() = openPolygonList[i];
    }

    //Remove all the tiny polygons, or polygons that are not closed. As they do not contribute to the actual print.
    int snapDistance = MM2INT(1.0);
    for(unsigned int i=0;i<polygonList.size();i++)
    {
        int length = 0;

        for(unsigned int n=1; n<polygonList[i].size(); n++)
        {
            length += vSize(polygonList[i][n] - polygonList[i][n-1]);
            if (length > snapDistance)
                break;
        }
        if (length < snapDistance)
        {
            polygonList.remove(i);
            i--;
        }
    }

    //Finally optimize all the polygons. Every point removed saves time in the long run.
    optimizePolygons(polygonList);

    int xy_offset = mesh->getSettingInMicrons("xy_offset");
    if (xy_offset != 0)
    {
        polygonList = polygonList.offset(xy_offset);
    }
}
Exemple #5
0
void combineInfillLayers(SliceMeshStorage& mesh, unsigned int amount)
{
    if (mesh.layers.empty() || mesh.layers.size() - 1 < static_cast<size_t>(mesh.getSettingAsCount("top_layers")) || mesh.getSettingAsCount("infill_line_distance") <= 0) //No infill is even generated.
    {
        return;
    }
    if(amount <= 1) //If we must combine 1 layer, nothing needs to be combined. Combining 0 layers is invalid.
    {
        return;
    }
    /* We need to round down the layer index we start at to the nearest
    divisible index. Otherwise we get some parts that have infill at divisible
    layers and some at non-divisible layers. Those layers would then miss each
    other. */
    size_t min_layer = mesh.getSettingAsCount("bottom_layers") + amount - 1;
    min_layer -= min_layer % amount; //Round upwards to the nearest layer divisible by infill_sparse_combine.
    size_t max_layer = mesh.layers.size() - 1 - mesh.getSettingAsCount("top_layers");
    max_layer -= max_layer % amount; //Round downwards to the nearest layer divisible by infill_sparse_combine.
    for(size_t layer_idx = min_layer;layer_idx <= max_layer;layer_idx += amount) //Skip every few layers, but extrude more.
    {
        SliceLayer* layer = &mesh.layers[layer_idx];
        for(unsigned int combine_count_here = 1; combine_count_here < amount; combine_count_here++)
        {
            if(layer_idx < combine_count_here)
            {
                break;
            }

            size_t lower_layer_idx = layer_idx - combine_count_here;
            if (lower_layer_idx < min_layer)
            {
                break;
            }
            SliceLayer* lower_layer = &mesh.layers[lower_layer_idx];
            for (SliceLayerPart& part : layer->parts)
            {
                for (unsigned int density_idx = 0; density_idx < part.infill_area_per_combine_per_density.size(); density_idx++)
                { // go over each density of gradual infill (these density areas overlap!)
                    std::vector<Polygons>& infill_area_per_combine = part.infill_area_per_combine_per_density[density_idx];
                    Polygons result;
                    for (SliceLayerPart& lower_layer_part : lower_layer->parts)
                    {
                        if (part.boundaryBox.hit(lower_layer_part.boundaryBox))
                        {

                            Polygons intersection = infill_area_per_combine[combine_count_here - 1].intersection(lower_layer_part.infill_area).offset(-200).offset(200);
                            result.add(intersection); // add area to be thickened
                            infill_area_per_combine[combine_count_here - 1] = infill_area_per_combine[combine_count_here - 1].difference(intersection); // remove thickened area from less thick layer here
                            if (density_idx < lower_layer_part.infill_area_per_combine_per_density.size())
                            { // only remove from *same density* areas on layer below
                                // If there are no same density areas, then it's ok to print them anyway
                                // Don't remove other density areas
                                unsigned int lower_density_idx = density_idx;
                                std::vector<Polygons>& lower_infill_area_per_combine = lower_layer_part.infill_area_per_combine_per_density[lower_density_idx];
                                lower_infill_area_per_combine[0] = lower_infill_area_per_combine[0].difference(intersection); // remove thickened area from lower (thickened) layer
                            }
                        }
                    }

                    infill_area_per_combine.push_back(result);
                }
            }
        }
    }
}
Exemple #6
0
void generateSkinAreas(int layer_nr, SliceMeshStorage& mesh, const int innermost_wall_line_width, int downSkinCount, int upSkinCount, int wall_line_count, bool no_small_gaps_heuristic)
{
    SliceLayer& layer = mesh.layers[layer_nr];
    
    if (downSkinCount == 0 && upSkinCount == 0)
    {
        return;
    }
    
    for(unsigned int partNr = 0; partNr < layer.parts.size(); partNr++)
    {
        SliceLayerPart& part = layer.parts[partNr];

        if (int(part.insets.size()) < wall_line_count)
        {
            continue; // the last wall is not present, the part should only get inter perimeter gaps, but no skin.
        }

        Polygons upskin = part.insets.back().offset(-innermost_wall_line_width / 2);
        Polygons downskin = (downSkinCount == 0) ? Polygons() : upskin;
        if (upSkinCount == 0) upskin = Polygons();

        auto getInsidePolygons = [&part, wall_line_count](SliceLayer& layer2)
            {
                Polygons result;
                for(SliceLayerPart& part2 : layer2.parts)
                {
                    if (part.boundaryBox.hit(part2.boundaryBox))
                    {
                        unsigned int wall_idx = std::max(0, std::min(wall_line_count, (int) part2.insets.size()) - 1);
                        result.add(part2.insets[wall_idx]);
                    }
                }
                return result;
            };
            
        if (no_small_gaps_heuristic)
        {
            if (static_cast<int>(layer_nr - downSkinCount) >= 0)
            {
                downskin = downskin.difference(getInsidePolygons(mesh.layers[layer_nr - downSkinCount])); // skin overlaps with the walls
            }
            
            if (static_cast<int>(layer_nr + upSkinCount) < static_cast<int>(mesh.layers.size()))
            {
                upskin = upskin.difference(getInsidePolygons(mesh.layers[layer_nr + upSkinCount])); // skin overlaps with the walls
            }
        }
        else 
        {
            if (layer_nr >= downSkinCount && downSkinCount > 0)
            {
                Polygons not_air = getInsidePolygons(mesh.layers[layer_nr - 1]);
                for (int downskin_layer_nr = layer_nr - downSkinCount; downskin_layer_nr < layer_nr - 1; downskin_layer_nr++)
                {
                    not_air = not_air.intersection(getInsidePolygons(mesh.layers[downskin_layer_nr]));
                }
                downskin = downskin.difference(not_air); // skin overlaps with the walls
            }
            
            if (layer_nr < static_cast<int>(mesh.layers.size()) - 1 - upSkinCount && upSkinCount > 0)
            {
                Polygons not_air = getInsidePolygons(mesh.layers[layer_nr + 1]);
                for (int upskin_layer_nr = layer_nr + 2; upskin_layer_nr < layer_nr + upSkinCount + 1; upskin_layer_nr++)
                {
                    not_air = not_air.intersection(getInsidePolygons(mesh.layers[upskin_layer_nr]));
                }
                upskin = upskin.difference(not_air); // skin overlaps with the walls
            }
        }
        
        Polygons skin = upskin.unionPolygons(downskin);
        
        skin.removeSmallAreas(MIN_AREA_SIZE);
        
        for (PolygonsPart& skin_area_part : skin.splitIntoParts())
        {
            part.skin_parts.emplace_back();
            part.skin_parts.back().outline = skin_area_part;
        }
    }
}
Exemple #7
0
void Weaver::weave(MeshGroup* meshgroup)
{   
    wireFrame.meshgroup = meshgroup;
    
    int maxz = meshgroup->max().z;

    int layer_count = (maxz - initial_layer_thickness) / connectionHeight + 1;

    std::cerr << "Layer count: " << layer_count << "\n";

    std::vector<cura::Slicer*> slicerList;

    for(Mesh& mesh : meshgroup->meshes)
    {
        cura::Slicer* slicer = new cura::Slicer(&mesh, initial_layer_thickness, connectionHeight, layer_count, mesh.getSettingBoolean("meshfix_keep_open_polygons"), mesh.getSettingBoolean("meshfix_extensive_stitching"));
        slicerList.push_back(slicer);
    }

    int starting_layer_idx;
    { // find first non-empty layer
        for (starting_layer_idx = 0; starting_layer_idx < layer_count; starting_layer_idx++)
        {
            Polygons parts;
            for (cura::Slicer* slicer : slicerList)
                parts.add(slicer->layers[starting_layer_idx].polygons);  
            
            if (parts.size() > 0)
                break;
        }
        if (starting_layer_idx > 0)
        {
            logWarning("First %i layers are empty!\n", starting_layer_idx);
        }
    }
    
    
    std::cerr<< "chainifying layers..." << std::endl;
    {
        int starting_z = -1;
        for (cura::Slicer* slicer : slicerList)
            wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygons);
        
        CommandSocket::sendPolygons(PrintFeatureType::OuterWall, /*0,*/ wireFrame.bottom_outline, 1);
        
        if (slicerList.empty()) //Wait, there is nothing to slice.
        {
            wireFrame.z_bottom = 0;
        }
        else
        {
            wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z;
        }
        
        Point starting_point_in_layer;
        if (wireFrame.bottom_outline.size() > 0)
            starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2;
        else 
            starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2;
        
        Progress::messageProgressStage(Progress::Stage::INSET_SKIN, nullptr);
        for (int layer_idx = starting_layer_idx + 1; layer_idx < layer_count; layer_idx++)
        {
            Progress::messageProgress(Progress::Stage::INSET_SKIN, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine
            
            Polygons parts1;
            for (cura::Slicer* slicer : slicerList)
                parts1.add(slicer->layers[layer_idx].polygons);

            
            Polygons chainified;

            chainify_polygons(parts1, starting_point_in_layer, chainified, false);
            
            CommandSocket::sendPolygons(PrintFeatureType::OuterWall, /*layer_idx - starting_layer_idx,*/ chainified, 1);

            if (chainified.size() > 0)
            {
                if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z;
                wireFrame.layers.emplace_back();
                WeaveLayer& layer = wireFrame.layers.back();
                
                layer.z0 = slicerList[0]->layers[layer_idx-1].z - starting_z;
                layer.z1 = slicerList[0]->layers[layer_idx].z - starting_z;
                
                layer.supported = chainified;
                
                starting_point_in_layer = layer.supported.back().back();
            }
        }
    }
    
    std::cerr<< "finding horizontal parts..." << std::endl;
    {
        Polygons* lower_top_parts = &wireFrame.bottom_outline;
        
        Progress::messageProgressStage(Progress::Stage::SUPPORT, nullptr);
        for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++)
        {
            Progress::messageProgress(Progress::Stage::SUPPORT, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine
            
            WeaveLayer& layer = wireFrame.layers[layer_idx];
            
            Polygons empty;
            Polygons& layer_above = (layer_idx+1 < wireFrame.layers.size())? wireFrame.layers[layer_idx+1].supported : empty;
            
            createHorizontalFill(*lower_top_parts, layer, layer_above, layer.z1);
            lower_top_parts = &layer.supported;
        }
    }
    // at this point layer.supported still only contains the polygons to be connected
    // when connecting layers, we further add the supporting polygons created by the roofs
    
    std::cerr<< "connecting layers..." << std::endl;
    {
        Polygons* lower_top_parts = &wireFrame.bottom_outline;
        int last_z = wireFrame.z_bottom;
        for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) // use top of every layer but the last
        {
            WeaveLayer& layer = wireFrame.layers[layer_idx];
            
            connect_polygons(*lower_top_parts, last_z, layer.supported, layer.z1, layer);
            layer.supported.add(layer.roofs.roof_outlines);
            lower_top_parts = &layer.supported;
            
            last_z = layer.z1;
        }
    }


    { // roofs:
        if (!wireFrame.layers.empty()) //If there are no layers, create no roof.
        {
            WeaveLayer& top_layer = wireFrame.layers.back();
            Polygons to_be_supported; // empty for the top layer
            fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs);
        }
    }
    
    
    { // bottom:
        if (!wireFrame.layers.empty()) //If there are no layers, create no bottom.
        {
            Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported)
            fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill);
        }
    }
    
}
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");
    }
}
Exemple #9
0
void generateSkirt(SliceDataStorage& storage, int distance, int extrusionWidth, int count, int minLength)
{
    if (count == 0) return;
    
    bool externalOnly = (distance > 0);
    
    Polygons support;
    if (storage.support.generated) 
        support = storage.support.supportLayers[0].supportAreas;
    { // get support polygons
        for(SliceMeshStorage& mesh : storage.meshes)
        {
            if (mesh.layers.size() < 1) continue;
            SliceLayer* layer = &mesh.layers[0];
            for(unsigned int i=0; i<layer->parts.size(); i++)        
                support = support.difference(layer->parts[i].outline);
        }
        
        // expand and contract to smooth the final polygon
        if (count == 1 && distance > 0)
        {
            int dist = extrusionWidth * 5;
            support = support.offset(dist).offset(-dist);
        }
    }
    
    int overshoot = 0; // distance by which to expand and contract the skirt to approximate the convex hull of the first layer
    if (count == 1 && distance > 0)
    {
        overshoot = 100000; // 10 cm 
    } 
    
    
    for(int skirtNr=0; skirtNr<count;skirtNr++)
    {
        int offsetDistance = distance + extrusionWidth * skirtNr + extrusionWidth / 2 + overshoot;

        Polygons skirtPolygons(storage.wipeTower.offset(offsetDistance));
        for(SliceMeshStorage& mesh : storage.meshes)
        {
            if (mesh.layers.size() < 1) continue;
            for(SliceLayerPart& part : mesh.layers[0].parts)
            {
                if (externalOnly)
                {
                    Polygons p;
                    p.add(part.outline.outerPolygon());
                    skirtPolygons = skirtPolygons.unionPolygons(p.offset(offsetDistance, ClipperLib::jtRound));
                }
                else
                {
                    skirtPolygons = skirtPolygons.unionPolygons(part.outline.offset(offsetDistance, ClipperLib::jtRound));
                }
            }
        }

        skirtPolygons = skirtPolygons.unionPolygons(support.offset(offsetDistance, ClipperLib::jtRound));
        //Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
        for(unsigned int n=0; n<skirtPolygons.size(); n++)
        {
            double area = skirtPolygons[n].area();
            if (area < 0 && area > -extrusionWidth * extrusionWidth * 100)
                skirtPolygons.remove(n--);
        }

        storage.skirt.add(skirtPolygons);

        int lenght = storage.skirt.polygonLength();
        if (skirtNr + 1 >= count && lenght > 0 && lenght < minLength) // make brim have more lines when total length is too small
            count++;
    }


    //Add a skirt under the wipetower to make it stick better.
    Polygons wipe_tower = storage.wipeTower.offset(-extrusionWidth / 2);
    while(wipe_tower.size() > 0)
    {
        storage.skirt.add(wipe_tower);
        wipe_tower = wipe_tower.offset(-extrusionWidth);
    }

    if (overshoot > 0)
    {
        storage.skirt = storage.skirt.offset(-overshoot, ClipperLib::jtRound);
    }
}