Example #1
0
void PrimeTower::generateWipeLocations(const SliceDataStorage& storage)
{
    wipe_from_middle = is_hollow;
    // only wipe from the middle of the prime tower if we have a z hop already on the first move after the layer switch
    for (int extruder_nr = 0; extruder_nr < storage.meshgroup->getExtruderCount(); extruder_nr++)
    {
        const ExtruderTrain& train = *storage.meshgroup->getExtruderTrain(extruder_nr);
        wipe_from_middle &= train.getSettingBoolean("retraction_hop_enabled") 
                        && (!train.getSettingBoolean("retraction_hop_only_when_collides") || train.getSettingBoolean("retraction_hop_after_extruder_switch"));
    }

    PolygonsPointIndex segment_start; // from where to start the sequence of wipe points
    PolygonsPointIndex segment_end; // where to end the sequence of wipe points

    if (wipe_from_middle)
    {
        // take the same start as end point so that the whole poly os covered.
        // find the inner polygon.
        segment_start = segment_end = PolygonUtils::findNearestVert(middle, ground_poly);
    }
    else
    {
        // take the closer corner of the wipe tower and generate wipe locations on that side only:
        //
        //     |
        //     |
        //     +-----
        //  .
        //  ^ nozzle switch location
        Point from = getLocationBeforePrimeTower(storage);

        // find the single line segment closest to [from] pointing most toward [from]
        PolygonsPointIndex closest_vert = PolygonUtils::findNearestVert(from, ground_poly);
        PolygonsPointIndex prev = closest_vert.prev();
        PolygonsPointIndex next = closest_vert.next();
        int64_t prev_dot_score = dot(from - closest_vert.p(), turn90CCW(prev.p() - closest_vert.p()));
        int64_t next_dot_score = dot(from - closest_vert.p(), turn90CCW(closest_vert.p() - next.p()));
        if (prev_dot_score > next_dot_score)
        {
            segment_start = prev;
            segment_end = closest_vert;
        }
        else
        {
            segment_start = closest_vert;
            segment_end = next;
        }
    }

    PolygonUtils::spreadDots(segment_start, segment_end, number_of_pre_wipe_locations, pre_wipe_locations);
}
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh)
{
    if (mesh.getSettingAsCount("wall_line_count") == 0)
    {
        return;
    }
    int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness");
    int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist");
    int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
    int64_t range_random_point_dist = avg_dist_between_points / 2;
    for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++)
    {
        SliceLayer& layer = mesh.layers[layer_nr];
        for (SliceLayerPart& part : layer.parts)
        {
            Polygons results;
            Polygons& skin = (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0];
            for (PolygonRef poly : skin)
            {
                // generate points in between p0 and p1
                PolygonRef result = results.newPoly();
                
                int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point
                Point* p0 = &poly.back();
                for (Point& p1 : poly)
                { // 'a' is the (next) new point between p0 and p1
                    Point p0p1 = p1 - *p0;
                    int64_t p0p1_size = vSize(p0p1);    
                    int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
                    for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist)
                    {
                        int r = rand() % (fuzziness * 2) - fuzziness;
                        Point perp_to_p0p1 = turn90CCW(p0p1);
                        Point fuzz = normal(perp_to_p0p1, r);
                        Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz;
                        result.add(pa);
                        dist_last_point = p0pa_dist;
                    }
                    dist_left_over = p0p1_size - dist_last_point;
                    
                    p0 = &p1;
                }
                while (result.size() < 3 )
                {
                    unsigned int point_idx = poly.size() - 2;
                    result.add(poly[point_idx]);
                    if (point_idx == 0) { break; }
                    point_idx--;
                }
                if (result.size() < 3)
                {
                    result.clear();
                    for (Point& p : poly)
                        result.add(p);
                }
            }
            skin = results;
        }
    }
}
bool MergeInfillLines::isConvertible(const Point& a, const Point& b, const Point& c, const Point& d, int64_t line_width, Point& first_middle, Point& second_middle, int64_t& resulting_line_width, bool use_second_middle_as_first)
{
    use_second_middle_as_first = false;
    int64_t max_line_width = nozzle_size * 3 / 2;

    Point ab = b - a;
    Point cd = d - c;

    if (b == c)
    {
        return false; // the line segments are connected!
    }

    int64_t ab_size = vSize(ab);
    int64_t cd_size = vSize(cd);

    if (ab_size > nozzle_size * 5 || cd_size > nozzle_size * 5)
    {
        return false; // infill lines are too long; otherwise infill lines might be merged when the next infill line is coincidentally shorter like |, would become \ ...
    }

    // if the lines are in the same direction then abs( dot(ab,cd) / |ab| / |cd| ) == 1
    int64_t prod = dot(ab,cd);
    if (std::abs(prod) + 400 < ab_size * cd_size) // 400 = 20*20, where 20 micron is the allowed inaccuracy in the dot product, introduced by the inaccurate point locations of a,b,c,d
    {
        return false; // extrusion moves not in the same or opposite diraction
    }
    
    // make lines in the same direction by flipping one
    if (prod < 0)
    {
        ab = ab * -1;
    }
    else if (prod == 0)
    {
        return false; // lines are orthogonal!
    }
    else if (b == d || a == c)
    {
        return false; // the line segments are connected!
    }

    first_middle = (use_second_middle_as_first)?
                    second_middle :
                    (a + b) / 2;
    second_middle = (c + d) / 2;
    
    Point dir_vector_perp = turn90CCW(second_middle - first_middle);
    int64_t dir_vector_perp_length = vSize(dir_vector_perp); // == dir_vector_length
    if (dir_vector_perp_length == 0)
    {
        return false;
    }
    if (dir_vector_perp_length > 5 * nozzle_size)
    {
        return false; // infill lines too far apart
    }

    Point infill_vector = (cd + ab) / 2; // (similar to) average line / direction of the infill

    // compute the resulting line width
    resulting_line_width = std::abs( dot(dir_vector_perp, infill_vector) / dir_vector_perp_length );
    if (resulting_line_width > max_line_width)
    {
        return false; // combined lines would be too wide
    }
    if (resulting_line_width == 0)
    {
        return false; // dot is zero, so lines are in each others extension, not next to eachother
    }

    // check whether two lines are adjacent (note: not 'line segments' but 'lines')
    Point ac = c - first_middle;
    Point infill_vector_perp = turn90CCW(infill_vector);
    int64_t perp_proj = dot(ac, infill_vector_perp);
    int64_t infill_vector_perp_length = vSize(infill_vector_perp);
    if (std::abs(std::abs(perp_proj) / infill_vector_perp_length - line_width) > 20) // it should be the case that dot(ac, infill_vector_perp) / |infill_vector_perp| == line_width
    {
        return false; // lines are too far apart or too close together
    }
    
    // check whether the two line segments are adjacent.
    // full infill in a narrow area might result in line segments with arbitrary distance between them
    // the more the narrow passage in the area gets aligned with the infill direction, the further apart the line segments will be
    // however, distant line segments might also be due to different narrow passages, so we limit the distance between merged line segments.
    if (!LinearAlg2D::lineSegmentsAreCloserThan(a, b, c, d, line_width * 2))
    {
        return false;
    }

    return true;
};
Example #4
0
void LineOrderOptimizer::optimize()
{
    int gridSize = 5000; // the size of the cells in the hash grid. TODO
    SparsePointGridInclusive<unsigned int> line_bucket_grid(gridSize);
    bool picked[polygons.size()];
    memset(picked, false, sizeof(bool) * polygons.size());/// initialized as falses
    
    for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) /// find closest point to initial starting point within each polygon +initialize picked
    {
        int best_point_idx = -1;
        float best_point_dist = std::numeric_limits<float>::infinity();
        PolygonRef poly = polygons[poly_idx];
        for (unsigned int point_idx = 0; point_idx < poly.size(); point_idx++) /// get closest point from polygon
        {
            float dist = vSize2f(poly[point_idx] - startPoint);
            if (dist < best_point_dist)
            {
                best_point_idx = point_idx;
                best_point_dist = dist;
            }
        }
        polyStart.push_back(best_point_idx);

        assert(poly.size() == 2);

        line_bucket_grid.insert(poly[0], poly_idx);
        line_bucket_grid.insert(poly[1], poly_idx);

    }


    Point incoming_perpundicular_normal(0, 0);
    Point prev_point = startPoint;
    for (unsigned int order_idx = 0; order_idx < polygons.size(); order_idx++) /// actual path order optimizer
    {
        int best_line_idx = -1;
        float best_score = std::numeric_limits<float>::infinity(); // distance score for the best next line

        /// check if single-line-polygon is close to last point
        for(unsigned int close_line_idx :
                line_bucket_grid.getNearbyVals(prev_point, gridSize))
        {
            if (picked[close_line_idx] || polygons[close_line_idx].size() < 1)
            {
                continue;
            }

            updateBestLine(close_line_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);
        }

        if (best_line_idx == -1) /// if single-line-polygon hasn't been found yet
        {
            for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
            {
                if (picked[poly_idx] || polygons[poly_idx].size() < 1) /// skip single-point-polygons
                {
                    continue;
                }
                assert(polygons[poly_idx].size() == 2);

                updateBestLine(poly_idx, best_line_idx, best_score, prev_point, incoming_perpundicular_normal);

            }
        }

        if (best_line_idx > -1) /// should always be true; we should have been able to identify the best next polygon
        {
            PolygonRef best_line = polygons[best_line_idx];
            assert(best_line.size() == 2);

            int line_start_point_idx = polyStart[best_line_idx];
            int line_end_point_idx = line_start_point_idx * -1 + 1; /// 1 -> 0 , 0 -> 1
            Point& line_start = best_line[line_start_point_idx];
            Point& line_end = best_line[line_end_point_idx];
            prev_point = line_end;
            incoming_perpundicular_normal = turn90CCW(normal(line_end - line_start, 1000));

            picked[best_line_idx] = true;
            polyOrder.push_back(best_line_idx);
        }
        else
        {
            logError("Failed to find next closest line.\n");
        }
    }
}