<commit_before>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "utils/linearAlg2D.h" //Distance from point to line.
#include "MergeInfillLines.h"

namespace cura
{

    MergeInfillLines::MergeInfillLines(ExtruderPlan& plan, const coord_t nozzle_size, const coord_t maximum_resolution) : extruder_plan(plan), nozzle_size(nozzle_size), maximum_resolution(maximum_resolution)
    {
        //Just copy the parameters to their fields.
    }

    coord_t MergeInfillLines::calcPathLength(const Point path_start, GCodePath& path) const
    {
        Point previous_point = path_start;
        coord_t result = 0;
        for (const Point point : path.points)
        {
            result += vSize(point - previous_point);
            previous_point = point;
        }
        return result;
    }

    /*
     * first_is_already_merged == false
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->   -(t)-o-----o
     *   /     /
     *  /     /
     * o     o
     *
     * travel (t) to first location is done through first_path_start_changed and new_first_path_start.
     * this gets rid of the tiny "blips". Depending on the merged line distance a small gap may appear, but this is
     * accounted for in the volume.
     *
     * first_is_already_merged == true
     *
     *                   o
     *                  /
     *                 /
     *    o-----o  +  /     --->    o-----------o   or with slight   o-----o-----o
     *   /           /             /                   bend         /
     *  /           /             /                                /
     * o           o             o                                o
     *
     */
    bool MergeInfillLines::mergeLinesSideBySide(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        Point average_first_path;

        coord_t first_path_length = calcPathLength(first_path_start, first_path);
        coord_t first_path_length_flow = first_path_length * first_path.flow; //To get the volume we don't need to include the line width since it's the same for both lines.
        const coord_t line_width = first_path.config->getLineWidth();

        if (first_is_already_merged)
        {
            // take second point of path, first_path.points[0]
            average_first_path = first_path.points[0];
        }
        else
        {
            average_first_path += first_path_start;
            for (const Point point : first_path.points)
            {
                average_first_path += point;
            }
            average_first_path /= (first_path.points.size() + 1);
        }

        coord_t second_path_length = calcPathLength(second_path_start, second_path);
        Point average_second_path = second_path_start;
        for (const Point point : second_path.points)
        {
            average_second_path += point;
        }
        coord_t second_path_length_flow = second_path_length *= second_path.flow;
        average_second_path /= (coord_t) (second_path.points.size() + 1);

        // predict new length and flow and if the new flow is to big, don't merge. conditions in this part must exactly match the actual merging
        coord_t new_path_length = first_path_length;
        coord_t dist2_from_line = 0;
        coord_t new_error_area = 0;
        coord_t merged_part_length = 0;
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1) {
                dist2_from_line = LinearAlg2D::getDist2FromLine(average_second_path, first_path.points[first_path.points.size() - 2], first_path.points[first_path.points.size() - 1]);
                merged_part_length = vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
                new_error_area = sqrt(dist2_from_line) * merged_part_length / 2;
            }
            // The max error margin uses the meshfix_maximum_resolution setting
            if (first_path.points.size() > 1 && error_area + new_error_area < merged_part_length * maximum_resolution)
            {
                new_path_length -= vSize(first_path.points[first_path.points.size() - 2] - first_path.points[first_path.points.size() - 1]);
                new_path_length += vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
            }
            else
            {
                new_path_length += vSize(first_path.points[first_path.points.size() - 1] - average_second_path);
            }
        }
        else
        {
            new_path_length -= vSize(first_path.points.back() - first_path_start);
            new_path_length += vSize(average_second_path - average_first_path);
        }
        double new_flow = ((first_path_length_flow + second_path_length_flow) / static_cast<double>(new_path_length));
        if (new_flow > 2 * nozzle_size / line_width)  // line width becomes too wide.
        {
            return false;
        }

        // do the actual merging
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1 && error_area + new_error_area < line_width * line_width)
            {
                first_path.points[first_path.points.size() - 1] = average_second_path;
                error_area += new_error_area;
            } else {
                first_path.points.push_back(average_second_path);
                error_area = 0;
            }
        }
        else
        {
            new_first_path_start = average_first_path;
            first_path.points.clear();
            first_path.points.push_back(average_second_path);
            error_area = 0;
        }

        first_path.flow = static_cast<double>(first_path_length_flow + second_path_length_flow) / new_path_length;

        return true;
    }

    bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        const Point first_path_end = first_path.points.back();
        const Point second_path_end = second_path.points.back();
        const coord_t line_width = first_path.config->getLineWidth();

        if (vSize2(first_path.points.back() - second_path_start) < line_width * line_width)
        {
            return false;
        }

        //Lines may be adjacent side-by-side then.
        Point first_path_leave_point;
        coord_t merged_size2;
        if (first_is_already_merged)
        {
            first_path_leave_point = first_path.points.back();  // this is the point that's going to merge
        } else {
            first_path_leave_point = (first_path_start + first_path_end) / 2;
        }
        const Point second_path_destination_point = (second_path_start + second_path_end) / 2;
        const Point merged_direction = second_path_destination_point - first_path_leave_point;
        if (first_is_already_merged)
        {
            merged_size2 = vSize2(second_path_destination_point - first_path.points.back());  // check distance with last point in merged line that is to be replaced
        }
        else
        {
            merged_size2 = vSize2(merged_direction);
        }
        if (merged_size2 > 25 * line_width * line_width)
        {
            return false; //Lines are too far away from each other.
        }
        if (merged_direction.X == 0 && merged_direction.Y == 0)
        {
            return true;  // we can just disregard the second point as it's exactly at the leave point of the first path.
        }

        // Max 1 line width to the side of the merged_direction
        if (LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_end,   first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            //|| abs(dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000))) > 866000    // 866000 angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes
            )
        {
            return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off.
        }
        if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point)  // yes this can actually happen
        {
            return false;
        }

        return mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area);
    }

    bool MergeInfillLines::mergeInfillLines(std::vector<GCodePath>& paths, const Point& starting_position) const
    {
        /* Algorithm overview:
            1. Loop over all lines to see if they can be merged.
            1a. Check if two adjacent lines can be merged (skipping travel moves in
                between).
            1b. If they can, merge both lines into the first line.
            1c. If they are merged, check next that the first line can be merged
                with the line after the second line.
            2. Do a second iteration over all paths to remove the tombstones. */
        std::vector<size_t> remove_path_indices;
        std::set<size_t> is_merged;
        std::set<size_t> removed;  // keep track of what we already removed, so don't remove it again

        //For each two adjacent lines, see if they can be merged.
        size_t first_path_index = 0;
        Point first_path_start = Point(starting_position.X, starting_position.Y);  // this one is not going to be overwritten
        size_t second_path_index = 1;
        coord_t error_area = 0;

        for (; second_path_index < paths.size(); second_path_index++)
        {
            GCodePath& first_path = paths[first_path_index];
            GCodePath& second_path = paths[second_path_index];
            Point second_path_start = paths[second_path_index - 1].points.back();

            if (second_path.config->isTravelPath())
            {
                continue; //Skip travel paths, we're looking for the first non-travel path.
            }

            bool allow_try_merge = true;
            // see if we meet criteria to merge. should be: travel - path1 not travel - (...) - travel - path2 not travel - travel
            // we're checking the travels here
            if (first_path_index <= 1 || !paths[first_path_index - 1].isTravelPath())  // "<= 1" because we don't want the first travel being changed. That may introduce a hole somewhere
            {
                allow_try_merge = false;
            }
            if (second_path_index + 1 >= paths.size() || !paths[second_path_index + 1].isTravelPath())
            {
                allow_try_merge = false;
            }
            if (first_path.config->isTravelPath()) //Don't merge travel moves.
            {
                allow_try_merge = false;
            }
            if (first_path.config != second_path.config) //Only merge lines that have the same type.
            {
                allow_try_merge = false;
            }
            if (first_path.config->type != PrintFeatureType::Infill && first_path.config->type != PrintFeatureType::Skin) //Only merge skin and infill lines.
            {
                allow_try_merge = false;
            }
            const bool first_is_already_merged = is_merged.find(first_path_index) != is_merged.end();
            if ((!first_is_already_merged && first_path.points.size() > 1) || second_path.points.size() > 1)
            {
                // For now we only merge simple lines, not polylines, to keep it simple.
                // If the first line is already a merged line, then allow it.
                allow_try_merge = false;
            }

            Point new_first_path_start;
            if (allow_try_merge && tryMerge(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area))
            {
                if (!first_is_already_merged)
                {
                    paths[first_path_index - 1].points.back().X = new_first_path_start.X;
                    paths[first_path_index - 1].points.back().Y = new_first_path_start.Y;
                }
                /* If we combine two lines, the next path may also be merged into the fist line, so we do NOT update
                first_path_index. */
                for (size_t to_delete_index = first_path_index + 1; to_delete_index <= second_path_index; to_delete_index++)
                {
                    if (removed.find(to_delete_index) == removed.end())  // if there are line(s) between first and second, then those lines are already marked as to be deleted, only add the new line(s)
                    {
                        remove_path_indices.push_back(to_delete_index);
                        removed.insert(to_delete_index);
                    }
                }
                is_merged.insert(first_path_index);
            }
            else
            {
                /* If we do not combine, the next iteration we must simply merge the
                second path with the line after it. */
                first_path_index = second_path_index;
                first_path_start = second_path_start;
            }
        }

        //Delete all removed lines in one pass so that we need to move lines less often.
        if (!remove_path_indices.empty())
        {
            size_t path_index = remove_path_indices[0];
            for (size_t removed_position = 1; removed_position < remove_path_indices.size(); removed_position++)
            {
                for (; path_index < remove_path_indices[removed_position] - removed_position; path_index++)
                {
                    paths[path_index] = paths[path_index + removed_position]; //Shift all paths.
                }
            }
            for (; path_index < paths.size() - remove_path_indices.size(); path_index++) //Remaining shifts at the end.
            {
                paths[path_index] = paths[path_index + remove_path_indices.size()];
            }
            paths.erase(paths.begin() + path_index, paths.end());
            return true;
        }
        else
        {
            return false;
        }
    }
}//namespace cura
<commit_msg>Slightly cleaned up last commit.<commit_after>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "utils/linearAlg2D.h" //Distance from point to line.
#include "MergeInfillLines.h"

namespace cura
{

    MergeInfillLines::MergeInfillLines(ExtruderPlan& plan, const coord_t nozzle_size, const coord_t maximum_resolution) : extruder_plan(plan), nozzle_size(nozzle_size), maximum_resolution(maximum_resolution)
    {
        //Just copy the parameters to their fields.
    }

    coord_t MergeInfillLines::calcPathLength(const Point path_start, GCodePath& path) const
    {
        Point previous_point = path_start;
        coord_t result = 0;
        for (const Point point : path.points)
        {
            result += vSize(point - previous_point);
            previous_point = point;
        }
        return result;
    }

    /*
     * first_is_already_merged == false
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->   -(t)-o-----o
     *   /     /
     *  /     /
     * o     o
     *
     * travel (t) to first location is done through first_path_start_changed and new_first_path_start.
     * this gets rid of the tiny "blips". Depending on the merged line distance a small gap may appear, but this is
     * accounted for in the volume.
     *
     * first_is_already_merged == true
     *
     *                   o
     *                  /
     *                 /
     *    o-----o  +  /     --->    o-----------o   or with slight   o-----o-----o
     *   /           /             /                   bend         /
     *  /           /             /                                /
     * o           o             o                                o
     *
     */
    bool MergeInfillLines::mergeLinesSideBySide(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        Point average_first_path;

        coord_t first_path_length = calcPathLength(first_path_start, first_path);
        coord_t first_path_length_flow = first_path_length * first_path.flow; //To get the volume we don't need to include the line width since it's the same for both lines.
        const coord_t line_width = first_path.config->getLineWidth();

        if (first_is_already_merged)
        {
            // take second point of path, first_path.points[0]
            average_first_path = first_path.points[0];
        }
        else
        {
            average_first_path += first_path_start;
            for (const Point point : first_path.points)
            {
                average_first_path += point;
            }
            average_first_path /= (first_path.points.size() + 1);
        }

        coord_t second_path_length = calcPathLength(second_path_start, second_path);
        Point average_second_path = second_path_start;
        for (const Point point : second_path.points)
        {
            average_second_path += point;
        }
        coord_t second_path_length_flow = second_path_length *= second_path.flow;
        average_second_path /= (coord_t) (second_path.points.size() + 1);

        // predict new length and flow and if the new flow is to big, don't merge. conditions in this part must exactly match the actual merging
        coord_t new_path_length = first_path_length;
        coord_t dist2_from_line = 0;
        coord_t new_error_area = 0;
        coord_t merged_part_length = 0;
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1) {
                dist2_from_line = LinearAlg2D::getDist2FromLine(average_second_path, first_path.points[first_path.points.size() - 2], first_path.points[first_path.points.size() - 1]);
                merged_part_length = vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
                new_error_area = sqrt(dist2_from_line) * merged_part_length / 2;
            }
            // The max error margin uses the meshfix_maximum_resolution setting
            if (first_path.points.size() > 1 && error_area + new_error_area < merged_part_length * maximum_resolution)
            {
                new_path_length -= vSize(first_path.points[first_path.points.size() - 2] - first_path.points[first_path.points.size() - 1]);
                new_path_length += vSize(first_path.points[first_path.points.size() - 2] - average_second_path);
            }
            else
            {
                new_path_length += vSize(first_path.points[first_path.points.size() - 1] - average_second_path);
            }
        }
        else
        {
            new_path_length -= vSize(first_path.points.back() - first_path_start);
            new_path_length += vSize(average_second_path - average_first_path);
        }
        double new_flow = ((first_path_length_flow + second_path_length_flow) / static_cast<double>(new_path_length));
        if (new_flow > 2 * nozzle_size / line_width)  // line width becomes too wide.
        {
            return false;
        }

        // do the actual merging
        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1 && error_area + new_error_area < line_width * line_width)
            {
                first_path.points[first_path.points.size() - 1] = average_second_path;
                error_area += new_error_area;
            } else {
                first_path.points.push_back(average_second_path);
                error_area = 0;
            }
        }
        else
        {
            new_first_path_start = average_first_path;
            first_path.points.clear();
            first_path.points.push_back(average_second_path);
            error_area = 0;
        }

        first_path.flow = static_cast<double>(first_path_length_flow + second_path_length_flow) / new_path_length;

        return true;
    }

    bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start, coord_t& error_area) const
    {
        const Point first_path_end = first_path.points.back();
        const Point second_path_end = second_path.points.back();
        const coord_t line_width = first_path.config->getLineWidth();

        if (vSize2(first_path_end - second_path_start) < line_width * line_width)
        {
            return false;
        }

        //Lines may be adjacent side-by-side then.
        Point first_path_leave_point;
        coord_t merged_size2;
        if (first_is_already_merged)
        {
            first_path_leave_point = first_path.points.back();  // this is the point that's going to merge
        } else {
            first_path_leave_point = (first_path_start + first_path_end) / 2;
        }
        const Point second_path_destination_point = (second_path_start + second_path_end) / 2;
        const Point merged_direction = second_path_destination_point - first_path_leave_point;
        if (first_is_already_merged)
        {
            merged_size2 = vSize2(second_path_destination_point - first_path.points.back());  // check distance with last point in merged line that is to be replaced
        }
        else
        {
            merged_size2 = vSize2(merged_direction);
        }
        if (merged_size2 > 25 * line_width * line_width)
        {
            return false; //Lines are too far away from each other.
        }
        if (merged_direction.X == 0 && merged_direction.Y == 0)
        {
            return true;  // we can just disregard the second point as it's exactly at the leave point of the first path.
        }

        // Max 1 line width to the side of the merged_direction
        if (LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_end,   first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            //|| abs(dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000))) > 866000    // 866000 angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes
            )
        {
            return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off.
        }
        if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point)  // yes this can actually happen
        {
            return false;
        }

        return mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area);
    }

    bool MergeInfillLines::mergeInfillLines(std::vector<GCodePath>& paths, const Point& starting_position) const
    {
        /* Algorithm overview:
            1. Loop over all lines to see if they can be merged.
            1a. Check if two adjacent lines can be merged (skipping travel moves in
                between).
            1b. If they can, merge both lines into the first line.
            1c. If they are merged, check next that the first line can be merged
                with the line after the second line.
            2. Do a second iteration over all paths to remove the tombstones. */
        std::vector<size_t> remove_path_indices;
        std::set<size_t> is_merged;
        std::set<size_t> removed;  // keep track of what we already removed, so don't remove it again

        //For each two adjacent lines, see if they can be merged.
        size_t first_path_index = 0;
        Point first_path_start = Point(starting_position.X, starting_position.Y);  // this one is not going to be overwritten
        size_t second_path_index = 1;
        coord_t error_area = 0;

        for (; second_path_index < paths.size(); second_path_index++)
        {
            GCodePath& first_path = paths[first_path_index];
            GCodePath& second_path = paths[second_path_index];
            Point second_path_start = paths[second_path_index - 1].points.back();

            if (second_path.config->isTravelPath())
            {
                continue; //Skip travel paths, we're looking for the first non-travel path.
            }

            bool allow_try_merge = true;
            // see if we meet criteria to merge. should be: travel - path1 not travel - (...) - travel - path2 not travel - travel
            // we're checking the travels here
            if (first_path_index <= 1 || !paths[first_path_index - 1].isTravelPath())  // "<= 1" because we don't want the first travel being changed. That may introduce a hole somewhere
            {
                allow_try_merge = false;
            }
            if (second_path_index + 1 >= paths.size() || !paths[second_path_index + 1].isTravelPath())
            {
                allow_try_merge = false;
            }
            if (first_path.config->isTravelPath()) //Don't merge travel moves.
            {
                allow_try_merge = false;
            }
            if (first_path.config != second_path.config) //Only merge lines that have the same type.
            {
                allow_try_merge = false;
            }
            if (first_path.config->type != PrintFeatureType::Infill && first_path.config->type != PrintFeatureType::Skin) //Only merge skin and infill lines.
            {
                allow_try_merge = false;
            }
            const bool first_is_already_merged = is_merged.find(first_path_index) != is_merged.end();
            if ((!first_is_already_merged && first_path.points.size() > 1) || second_path.points.size() > 1)
            {
                // For now we only merge simple lines, not polylines, to keep it simple.
                // If the first line is already a merged line, then allow it.
                allow_try_merge = false;
            }

            Point new_first_path_start;
            if (allow_try_merge && tryMerge(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start, error_area))
            {
                if (!first_is_already_merged)
                {
                    paths[first_path_index - 1].points.back().X = new_first_path_start.X;
                    paths[first_path_index - 1].points.back().Y = new_first_path_start.Y;
                }
                /* If we combine two lines, the next path may also be merged into the fist line, so we do NOT update
                first_path_index. */
                for (size_t to_delete_index = first_path_index + 1; to_delete_index <= second_path_index; to_delete_index++)
                {
                    if (removed.find(to_delete_index) == removed.end())  // if there are line(s) between first and second, then those lines are already marked as to be deleted, only add the new line(s)
                    {
                        remove_path_indices.push_back(to_delete_index);
                        removed.insert(to_delete_index);
                    }
                }
                is_merged.insert(first_path_index);
            }
            else
            {
                /* If we do not combine, the next iteration we must simply merge the
                second path with the line after it. */
                first_path_index = second_path_index;
                first_path_start = second_path_start;
            }
        }

        //Delete all removed lines in one pass so that we need to move lines less often.
        if (!remove_path_indices.empty())
        {
            size_t path_index = remove_path_indices[0];
            for (size_t removed_position = 1; removed_position < remove_path_indices.size(); removed_position++)
            {
                for (; path_index < remove_path_indices[removed_position] - removed_position; path_index++)
                {
                    paths[path_index] = paths[path_index + removed_position]; //Shift all paths.
                }
            }
            for (; path_index < paths.size() - remove_path_indices.size(); path_index++) //Remaining shifts at the end.
            {
                paths[path_index] = paths[path_index + remove_path_indices.size()];
            }
            paths.erase(paths.begin() + path_index, paths.end());
            return true;
        }
        else
        {
            return false;
        }
    }
}//namespace cura
<|endoftext|>


<commit_before>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "utils/linearAlg2D.h" //Distance from point to line.
#include "MergeInfillLines.h"

namespace cura
{

    MergeInfillLines::MergeInfillLines(ExtruderPlan& plan) : extruder_plan(plan)
    {
        //Just copy the parameters to their fields.
    }

    /*
     * first_is_already_merged == false, first_path_start_may_move == false
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->     o-----o
     *   /     /               /
     *  /     /               /
     * o     o               o
     *
     * first_is_already_merged == false, first_path_start_may_move == true
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->   -(t)-o-----o
     *   /     /
     *  /     /
     * o     o
     *
     * travel (t) to first location is done through first_path_start_changed and new_first_path_start.
     * this gets rid of the tiny "blips". Depending on the merged line distance a small gap may appear, but this is
     * accounted for in the volume.
     *
     * first_is_already_merged == true
     *
     *                   o
     *                  /
     *                 /
     *    o-----o  +  /     --->    o-----------o   or with slight   o-----o-----o
     *   /           /             /                   bend         /
     *  /           /             /                                /
     * o           o             o                                o
     *
     */
    void MergeInfillLines::mergeLinesSideBySide(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start) const
    {
        coord_t first_path_length_flow = 0;
        Point average_first_path;

        Point previous_point = first_path_start;
        for (const Point point : first_path.points)
        {
            first_path_length_flow += vSize(point - previous_point);
            previous_point = point;
        }
        first_path_length_flow *= first_path.flow; //To get the volume we don't need to include the line width since it's the same for both lines.

        if (first_is_already_merged)
        {
            // take second point of path, first_path.points[0]
            average_first_path = first_path.points[0];
        }
        else
        {
            average_first_path += first_path_start;
            for (const Point point : first_path.points)
            {
                average_first_path += point;
            }
            average_first_path /= (first_path.points.size() + 1);
        }

        coord_t second_path_length_flow = 0;
        Point previous_point_second = second_path_start;
        Point average_second_path = second_path_start;
        for (const Point point : second_path.points)
        {
            second_path_length_flow += vSize(point - previous_point_second);
            average_second_path += point;
            previous_point_second = point;
        }
        second_path_length_flow *= second_path.flow;
        average_second_path /= (second_path.points.size() + 1);

        coord_t new_path_length = 0;

        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1 && LinearAlg2D::getDist2FromLine(average_second_path, first_path.points[first_path.points.size()-2], first_path.points[first_path.points.size()-1]) == 0) {
                first_path.points[first_path.points.size()-1] = average_second_path;
            } else {
                first_path.points.push_back(average_second_path);
            }
        }
        else
        {
            first_path.points.clear();
            new_first_path_start = average_first_path;
            //first_path.points.push_back(average_first_path);
            first_path.points.push_back(average_second_path);
        }
        previous_point = first_path_start;
        for (const Point point : first_path.points)
        {
            new_path_length += vSize(point - previous_point);
            previous_point = point;
        }

        first_path.flow = static_cast<double>(first_path_length_flow + second_path_length_flow) / new_path_length;
    }

    bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start) const
    {
        if (first_path.config->isTravelPath()) //Don't merge travel moves.
        {
            return false;
        }
        if (first_path.config != second_path.config) //Only merge lines that have the same type.
        {
            return false;
        }
        if (first_path.config->type != PrintFeatureType::Infill && first_path.config->type != PrintFeatureType::Skin) //Only merge skin and infill lines.
        {
            return false;
        }
        if ((!first_is_already_merged && first_path.points.size() > 1) || second_path.points.size() > 1)
        {
            // For now we only merge simple lines, not polylines, to keep it simple.
            // If the first line is already a merged line, then allow it.
            return false;
        }

        const Point first_path_end = first_path.points.back();
        const Point second_path_end = second_path.points.back();
        const coord_t line_width = first_path.config->getLineWidth();

        //Lines may be adjacent side-by-side then.
        Point first_path_leave_point;
        coord_t merged_size2;
        if (first_is_already_merged) {
            first_path_leave_point = first_path.points.back();  // this is the point that's going to merge
        } else {
            first_path_leave_point = (first_path_start + first_path_end) / 2;
        }
        const Point second_path_destination_point = (second_path_start + second_path_end) / 2;
        const Point merged_direction = second_path_destination_point - first_path_leave_point;
        if (first_is_already_merged)
        {
            merged_size2 = vSize2(second_path_destination_point - first_path.points.back());  // check distance with last point in merged line that is to be replaced
        }
        else
        {
            merged_size2 = vSize2(merged_direction);
        }
        if (merged_size2 > 25 * line_width * line_width)
        {
            return false; //Lines are too far away from each other.
        }
        if (merged_direction.X == 0 && merged_direction.Y == 0)
        {
            return true;  // we can just disregard the second point as it's exactly at the leave point of the first path.
        }
        // Max 1 line width to the side of the merged_direction
        if (LinearAlg2D::getDist2FromLine(first_path_start,  second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_end,   first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width)
        {
            return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off.
        }
        if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point)  // yes this can actually happen
        {
            return false;
        }
        mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start);
        return true;
    }

    bool MergeInfillLines::mergeInfillLines(std::vector<GCodePath>& paths, const Point& starting_position) const
    {
        /* Algorithm overview:
            1. Loop over all lines to see if they can be merged.
            1a. Check if two adjacent lines can be merged (skipping travel moves in
                between).
            1b. If they can, merge both lines into the first line.
            1c. If they are merged, check next that the first line can be merged
                with the line after the second line.
            2. Do a second iteration over all paths to remove the tombstones. */
        std::vector<size_t> remove_path_indices;
        std::set<size_t> is_merged;
        std::set<size_t> removed;  // keep track of what we already removed, so don't remove it again

        //For each two adjacent lines, see if they can be merged.
        size_t first_path_index = 0;
        Point first_path_start = Point(starting_position.X, starting_position.Y);  // this one is not going to be overwritten
        size_t second_path_index = 1;

        for (; second_path_index < paths.size(); second_path_index++)
        {
            GCodePath& first_path = paths[first_path_index];
            GCodePath& second_path = paths[second_path_index];
            Point second_path_start = paths[second_path_index - 1].points.back();
            const coord_t line_width = first_path.config->getLineWidth();

            if (second_path.config->isTravelPath())
            {
                continue; //Skip travel paths, we're looking for the first non-travel path.
            }

            bool allow_try_merge = true;
            // see if we meet criteria to merge. should be: travel - path1 not travel - (...) - travel - path2 not travel - travel
            // we're checking the travels here
            if (first_path_index <= 1 || !paths[first_path_index - 1].isTravelPath())  // "<= 1" because we don't want the first travel being changed. That may introduce a hole somewhere
            {
                allow_try_merge = false;
            }
            if (second_path_index + 1 >= paths.size() || !paths[second_path_index + 1].isTravelPath())
            {
                allow_try_merge = false;
            }
            // don't do magic on already connecting lines
            if (vSize2(first_path.points.back() - second_path_start) < line_width * line_width)
            {
                allow_try_merge = false;
            }

            Point new_first_path_start;
            const bool first_is_already_merged = is_merged.find(first_path_index) != is_merged.end();
            if (allow_try_merge && tryMerge(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start))
            {
                if (!first_is_already_merged)
                {
                    paths[first_path_index - 1].points.back().X = new_first_path_start.X;
                    paths[first_path_index - 1].points.back().Y = new_first_path_start.Y;
                }
                /* If we combine two lines, the next path may also be merged into the fist line, so we do NOT update
                first_path_index. */
                for (size_t to_delete_index = first_path_index + 1; to_delete_index <= second_path_index; to_delete_index++)
                {
                    if (removed.find(to_delete_index) == removed.end())  // if there are line(s) between first and second, then those lines are already marked as to be deleted, only add the new line(s)
                    {
                        remove_path_indices.push_back(to_delete_index);
                        removed.insert(to_delete_index);
                    }
                }
                is_merged.insert(first_path_index);
            }
            else
            {
                /* If we do not combine, the next iteration we must simply merge the
                second path with the line after it. */
                first_path_index = second_path_index;
                first_path_start = second_path_start;
            }
        }

        //Delete all removed lines in one pass so that we need to move lines less often.
        if (!remove_path_indices.empty())
        {
            size_t path_index = remove_path_indices[0];
            for (size_t removed_position = 1; removed_position < remove_path_indices.size(); removed_position++)
            {
                for (; path_index < remove_path_indices[removed_position] - removed_position; path_index++)
                {
                    paths[path_index] = paths[path_index + removed_position]; //Shift all paths.
                }
            }
            for (; path_index < paths.size() - remove_path_indices.size(); path_index++) //Remaining shifts at the end.
            {
                paths[path_index] = paths[path_index + remove_path_indices.size()];
            }
            paths.erase(paths.begin() + path_index, paths.end());
            return true;
        }
        else
        {
            return false;
        }
    }
}//namespace cura
<commit_msg>Optimizing appearing holes. CURA-5535<commit_after>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "utils/linearAlg2D.h" //Distance from point to line.
#include "MergeInfillLines.h"

namespace cura
{

    MergeInfillLines::MergeInfillLines(ExtruderPlan& plan) : extruder_plan(plan)
    {
        //Just copy the parameters to their fields.
    }

    /*
     * first_is_already_merged == false, first_path_start_may_move == false
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->     o-----o
     *   /     /               /
     *  /     /               /
     * o     o               o
     *
     * first_is_already_merged == false, first_path_start_may_move == true
     *
     *       o     o
     *      /     /
     *     /     /
     *    /  +  /      --->   -(t)-o-----o
     *   /     /
     *  /     /
     * o     o
     *
     * travel (t) to first location is done through first_path_start_changed and new_first_path_start.
     * this gets rid of the tiny "blips". Depending on the merged line distance a small gap may appear, but this is
     * accounted for in the volume.
     *
     * first_is_already_merged == true
     *
     *                   o
     *                  /
     *                 /
     *    o-----o  +  /     --->    o-----------o   or with slight   o-----o-----o
     *   /           /             /                   bend         /
     *  /           /             /                                /
     * o           o             o                                o
     *
     */
    void MergeInfillLines::mergeLinesSideBySide(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start) const
    {
        coord_t first_path_length_flow = 0;
        Point average_first_path;

        Point previous_point = first_path_start;
        for (const Point point : first_path.points)
        {
            first_path_length_flow += vSize(point - previous_point);
            previous_point = point;
        }
        first_path_length_flow *= first_path.flow; //To get the volume we don't need to include the line width since it's the same for both lines.

        if (first_is_already_merged)
        {
            // take second point of path, first_path.points[0]
            average_first_path = first_path.points[0];
        }
        else
        {
            average_first_path += first_path_start;
            for (const Point point : first_path.points)
            {
                average_first_path += point;
            }
            average_first_path /= (first_path.points.size() + 1);
        }

        coord_t second_path_length_flow = 0;
        Point previous_point_second = second_path_start;
        Point average_second_path = second_path_start;
        for (const Point point : second_path.points)
        {
            second_path_length_flow += vSize(point - previous_point_second);
            average_second_path += point;
            previous_point_second = point;
        }
        second_path_length_flow *= second_path.flow;
        average_second_path /= (second_path.points.size() + 1);

        coord_t new_path_length = 0;

        if (first_is_already_merged)
        {
            // check if the new point is a good extension of last part of existing polyline
            // because of potential accumulation of errors introduced each time a line is merged, we do not allow any error.
            if (first_path.points.size() > 1 && LinearAlg2D::getDist2FromLine(average_second_path, first_path.points[first_path.points.size()-2], first_path.points[first_path.points.size()-1]) == 0) {
                first_path.points[first_path.points.size()-1] = average_second_path;
            } else {
                first_path.points.push_back(average_second_path);
            }
        }
        else
        {
            first_path.points.clear();
            new_first_path_start = average_first_path;
            //first_path.points.push_back(average_first_path);
            first_path.points.push_back(average_second_path);
        }
        previous_point = first_path_start;
        for (const Point point : first_path.points)
        {
            new_path_length += vSize(point - previous_point);
            previous_point = point;
        }

        first_path.flow = static_cast<double>(first_path_length_flow + second_path_length_flow) / new_path_length;
    }

    bool MergeInfillLines::tryMerge(const bool first_is_already_merged, GCodePath& first_path, const Point first_path_start, GCodePath& second_path, const Point second_path_start, Point& new_first_path_start) const
    {
        if (first_path.config->isTravelPath()) //Don't merge travel moves.
        {
            return false;
        }
        if (first_path.config != second_path.config) //Only merge lines that have the same type.
        {
            return false;
        }
        if (first_path.config->type != PrintFeatureType::Infill && first_path.config->type != PrintFeatureType::Skin) //Only merge skin and infill lines.
        {
            return false;
        }
        if ((!first_is_already_merged && first_path.points.size() > 1) || second_path.points.size() > 1)
        {
            // For now we only merge simple lines, not polylines, to keep it simple.
            // If the first line is already a merged line, then allow it.
            return false;
        }

        const Point first_path_end = first_path.points.back();
        const Point second_path_end = second_path.points.back();
        const coord_t line_width = first_path.config->getLineWidth();

        //Lines may be adjacent side-by-side then.
        Point first_path_leave_point;
        coord_t merged_size2;
        if (first_is_already_merged) {
            first_path_leave_point = first_path.points.back();  // this is the point that's going to merge
        } else {
            first_path_leave_point = (first_path_start + first_path_end) / 2;
        }
        const Point second_path_destination_point = (second_path_start + second_path_end) / 2;
        const Point merged_direction = second_path_destination_point - first_path_leave_point;
        if (first_is_already_merged)
        {
            merged_size2 = vSize2(second_path_destination_point - first_path.points.back());  // check distance with last point in merged line that is to be replaced
        }
        else
        {
            merged_size2 = vSize2(merged_direction);
        }
        if (merged_size2 > 25 * line_width * line_width)
        {
            return false; //Lines are too far away from each other.
        }
        if (merged_direction.X == 0 && merged_direction.Y == 0)
        {
            return true;  // we can just disregard the second point as it's exactly at the leave point of the first path.
        }

        // Max 1 line width to the side of the merged_direction
        if (LinearAlg2D::getDist2FromLine(first_path_start,  second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(first_path_end, second_path_destination_point, second_path_destination_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_start, first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || LinearAlg2D::getDist2FromLine(second_path_end,   first_path_leave_point, first_path_leave_point + merged_direction) > line_width * line_width
            || dot(normal(merged_direction, 1000), normal(second_path_end - second_path_start, 1000)) > 866000)  // angle of old second_path with new merged direction should not be too small (30 degrees), as it will introduce holes
        {
            return false; //One of the lines is too far from the merged line. Lines would be too wide or too far off.
        }
        if (first_is_already_merged && first_path.points.size() > 1 && first_path.points[first_path.points.size() - 2] == second_path_destination_point)  // yes this can actually happen
        {
            return false;
        }
        mergeLinesSideBySide(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start);
        return true;
    }

    bool MergeInfillLines::mergeInfillLines(std::vector<GCodePath>& paths, const Point& starting_position) const
    {
        /* Algorithm overview:
            1. Loop over all lines to see if they can be merged.
            1a. Check if two adjacent lines can be merged (skipping travel moves in
                between).
            1b. If they can, merge both lines into the first line.
            1c. If they are merged, check next that the first line can be merged
                with the line after the second line.
            2. Do a second iteration over all paths to remove the tombstones. */
        std::vector<size_t> remove_path_indices;
        std::set<size_t> is_merged;
        std::set<size_t> removed;  // keep track of what we already removed, so don't remove it again

        //For each two adjacent lines, see if they can be merged.
        size_t first_path_index = 0;
        Point first_path_start = Point(starting_position.X, starting_position.Y);  // this one is not going to be overwritten
        size_t second_path_index = 1;

        for (; second_path_index < paths.size(); second_path_index++)
        {
            GCodePath& first_path = paths[first_path_index];
            GCodePath& second_path = paths[second_path_index];
            Point second_path_start = paths[second_path_index - 1].points.back();
            const coord_t line_width = first_path.config->getLineWidth();

            if (second_path.config->isTravelPath())
            {
                continue; //Skip travel paths, we're looking for the first non-travel path.
            }

            bool allow_try_merge = true;
            // see if we meet criteria to merge. should be: travel - path1 not travel - (...) - travel - path2 not travel - travel
            // we're checking the travels here
            if (first_path_index <= 1 || !paths[first_path_index - 1].isTravelPath())  // "<= 1" because we don't want the first travel being changed. That may introduce a hole somewhere
            {
                allow_try_merge = false;
            }
            if (second_path_index + 1 >= paths.size() || !paths[second_path_index + 1].isTravelPath())
            {
                allow_try_merge = false;
            }
            // don't do magic on already connecting lines
            if (vSize2(first_path.points.back() - second_path_start) < line_width * line_width)
            {
                allow_try_merge = false;
            }

            Point new_first_path_start;
            const bool first_is_already_merged = is_merged.find(first_path_index) != is_merged.end();
            if (allow_try_merge && tryMerge(first_is_already_merged, first_path, first_path_start, second_path, second_path_start, new_first_path_start))
            {
                if (!first_is_already_merged)
                {
                    paths[first_path_index - 1].points.back().X = new_first_path_start.X;
                    paths[first_path_index - 1].points.back().Y = new_first_path_start.Y;
                }
                /* If we combine two lines, the next path may also be merged into the fist line, so we do NOT update
                first_path_index. */
                for (size_t to_delete_index = first_path_index + 1; to_delete_index <= second_path_index; to_delete_index++)
                {
                    if (removed.find(to_delete_index) == removed.end())  // if there are line(s) between first and second, then those lines are already marked as to be deleted, only add the new line(s)
                    {
                        remove_path_indices.push_back(to_delete_index);
                        removed.insert(to_delete_index);
                    }
                }
                is_merged.insert(first_path_index);
            }
            else
            {
                /* If we do not combine, the next iteration we must simply merge the
                second path with the line after it. */
                first_path_index = second_path_index;
                first_path_start = second_path_start;
            }
        }

        //Delete all removed lines in one pass so that we need to move lines less often.
        if (!remove_path_indices.empty())
        {
            size_t path_index = remove_path_indices[0];
            for (size_t removed_position = 1; removed_position < remove_path_indices.size(); removed_position++)
            {
                for (; path_index < remove_path_indices[removed_position] - removed_position; path_index++)
                {
                    paths[path_index] = paths[path_index + removed_position]; //Shift all paths.
                }
            }
            for (; path_index < paths.size() - remove_path_indices.size(); path_index++) //Remaining shifts at the end.
            {
                paths[path_index] = paths[path_index + remove_path_indices.size()];
            }
            paths.erase(paths.begin() + path_index, paths.end());
            return true;
        }
        else
        {
            return false;
        }
    }
}//namespace cura
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: SwXMLBlockExport.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: dvo $ $Date: 2001-06-29 21:13:25 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): Martin Gallwey (gallwey@sun.com)
 *
 *
 ************************************************************************/
#ifndef _SW_XMLBLOCKEXPORT_HXX
#include <SwXMLBlockExport.hxx>
#endif

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star;
using namespace ::rtl;
using ::xmloff::token::XMLTokenEnum;

sal_Char __READONLY_DATA sXML_np__block_list[] = "_block-list";

SwXMLBlockListExport::SwXMLBlockListExport( SwXMLTextBlocks & rBlocks, const rtl::OUString &rFileName,
          com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler> &rHandler)
: rBlockList(rBlocks),
  SvXMLExport(rFileName, rHandler)
{
    _GetNamespaceMap().AddAtIndex( XML_NAMESPACE_BLOCKLIST_IDX, sXML_np_block_list,
                                  sXML_n_block_list, XML_NAMESPACE_BLOCKLIST );
}

sal_uInt32 SwXMLBlockListExport::exportDoc(enum XMLTokenEnum eClass)
{
    GetDocHandler()->startDocument();

    sal_uInt16 nPos = _GetNamespaceMap().GetIndexByKey( XML_NAMESPACE_BLOCKLIST );
    AddAttribute ( USHRT_MAX, _GetNamespaceMap().GetAttrNameByIndex ( nPos ),
                              _GetNamespaceMap().GetNameByIndex ( nPos ) );
    AddAttribute( XML_NAMESPACE_BLOCKLIST, sXML_list_name,
                       OUString(rBlockList.GetName()));
    {
        SvXMLElementExport pRoot (*this, XML_NAMESPACE_BLOCKLIST, sXML_block_list, sal_True, sal_True);
        sal_uInt16 nBlocks= rBlockList.GetCount();
        for ( sal_uInt16 i = 0; i < nBlocks; i++)
        {
            AddAttribute( XML_NAMESPACE_BLOCKLIST,
                          sXML_abbreviated_name,
                          OUString(rBlockList.GetShortName(i)));
            AddAttribute( XML_NAMESPACE_BLOCKLIST,
                          sXML_package_name,
                          OUString(rBlockList.GetPackageName(i)));
            AddAttribute( XML_NAMESPACE_BLOCKLIST,
                          sXML_name,
                          OUString(rBlockList.GetLongName(i)));
            AddAttribute( XML_NAMESPACE_BLOCKLIST,
                          sXML_unformatted_text,
                          rBlockList.IsOnlyTextBlock(i) ?
                              OUString( RTL_CONSTASCII_USTRINGPARAM( sXML_true ) ) :
                            OUString( RTL_CONSTASCII_USTRINGPARAM( sXML_false ) ) );

            SvXMLElementExport aBlock( *this, XML_NAMESPACE_BLOCKLIST, sXML_block, sal_True, sal_True);
        }
    }
    GetDocHandler()->endDocument();
    return 0;
}

SwXMLTextBlockExport::SwXMLTextBlockExport( SwXMLTextBlocks & rBlocks, const rtl::OUString &rFileName,
          com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler> &rHandler)
: rBlockList(rBlocks),
  SvXMLExport(rFileName, rHandler)
{
    _GetNamespaceMap().AddAtIndex( XML_NAMESPACE_BLOCKLIST_IDX, sXML_np_block_list,
                                  sXML_n_block_list, XML_NAMESPACE_BLOCKLIST );
}

sal_uInt32 SwXMLTextBlockExport::exportDoc(const String &rText)
{
    GetDocHandler()->startDocument();

    sal_uInt16 nPos = _GetNamespaceMap().GetIndexByKey( XML_NAMESPACE_OFFICE );
    AddAttribute ( USHRT_MAX, _GetNamespaceMap().GetAttrNameByIndex ( nPos ),
                              _GetNamespaceMap().GetNameByIndex ( nPos ) );
    nPos = _GetNamespaceMap().GetIndexByKey( XML_NAMESPACE_TEXT );
    AddAttribute ( USHRT_MAX, _GetNamespaceMap().GetAttrNameByIndex ( nPos ),
                              _GetNamespaceMap().GetNameByIndex ( nPos ) );
    {
        SvXMLElementExport aDocument (*this, XML_NAMESPACE_OFFICE, sXML_document, sal_True, sal_True);
        {
            SvXMLElementExport aBody (*this, XML_NAMESPACE_OFFICE, sXML_body, sal_True, sal_True);
            {
                xub_StrLen nPos = 0;
                do
                {
                    String sTemp ( rText.GetToken( 0, '\015', nPos ) );
                     SvXMLElementExport aPara (*this, XML_NAMESPACE_TEXT, sXML_p, sal_True, sal_False);
                    GetDocHandler()->characters(sTemp);
                } while (STRING_NOTFOUND != nPos );
            }

        }
    }
    GetDocHandler()->endDocument();
    return 0;
}
<commit_msg>avoid assertion<commit_after>/*************************************************************************
 *
 *  $RCSfile: SwXMLBlockExport.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: mtg $ $Date: 2001-07-05 14:16:48 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): Martin Gallwey (gallwey@sun.com)
 *
 *
 ************************************************************************/
#ifndef _SW_XMLBLOCKEXPORT_HXX
#include <SwXMLBlockExport.hxx>
#endif

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star;
using namespace ::rtl;
using ::xmloff::token::XMLTokenEnum;

sal_Char __READONLY_DATA sXML_np__block_list[] = "_block-list";

SwXMLBlockListExport::SwXMLBlockListExport( SwXMLTextBlocks & rBlocks, const rtl::OUString &rFileName,
          com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler> &rHandler)
: rBlockList(rBlocks),
  SvXMLExport(rFileName, rHandler)
{
    _GetNamespaceMap().AddAtIndex( XML_NAMESPACE_BLOCKLIST_IDX, sXML_np_block_list,
                                  sXML_n_block_list, XML_NAMESPACE_BLOCKLIST );
}

sal_uInt32 SwXMLBlockListExport::exportDoc(enum XMLTokenEnum eClass)
{
    GetDocHandler()->startDocument();

    sal_uInt16 nPos = _GetNamespaceMap().GetIndexByKey( XML_NAMESPACE_BLOCKLIST );
    AddAttribute ( XML_NAMESPACE_NONE, _GetNamespaceMap().GetAttrNameByIndex ( nPos ),
                              _GetNamespaceMap().GetNameByIndex ( nPos ) );
    AddAttribute( XML_NAMESPACE_BLOCKLIST, sXML_list_name,
                       OUString(rBlockList.GetName()));
    {
        SvXMLElementExport pRoot (*this, XML_NAMESPACE_BLOCKLIST, sXML_block_list, sal_True, sal_True);
        sal_uInt16 nBlocks= rBlockList.GetCount();
        for ( sal_uInt16 i = 0; i < nBlocks; i++)
        {
            AddAttribute( XML_NAMESPACE_BLOCKLIST,
                          sXML_abbreviated_name,
                          OUString(rBlockList.GetShortName(i)));
            AddAttribute( XML_NAMESPACE_BLOCKLIST,
                          sXML_package_name,
                          OUString(rBlockList.GetPackageName(i)));
            AddAttribute( XML_NAMESPACE_BLOCKLIST,
                          sXML_name,
                          OUString(rBlockList.GetLongName(i)));
            AddAttribute( XML_NAMESPACE_BLOCKLIST,
                          sXML_unformatted_text,
                          rBlockList.IsOnlyTextBlock(i) ?
                              OUString( RTL_CONSTASCII_USTRINGPARAM( sXML_true ) ) :
                            OUString( RTL_CONSTASCII_USTRINGPARAM( sXML_false ) ) );

            SvXMLElementExport aBlock( *this, XML_NAMESPACE_BLOCKLIST, sXML_block, sal_True, sal_True);
        }
    }
    GetDocHandler()->endDocument();
    return 0;
}

SwXMLTextBlockExport::SwXMLTextBlockExport( SwXMLTextBlocks & rBlocks, const rtl::OUString &rFileName,
          com::sun::star::uno::Reference< com::sun::star::xml::sax::XDocumentHandler> &rHandler)
: rBlockList(rBlocks),
  SvXMLExport(rFileName, rHandler)
{
    _GetNamespaceMap().AddAtIndex( XML_NAMESPACE_BLOCKLIST_IDX, sXML_np_block_list,
                                  sXML_n_block_list, XML_NAMESPACE_BLOCKLIST );
}

sal_uInt32 SwXMLTextBlockExport::exportDoc(const String &rText)
{
    GetDocHandler()->startDocument();

    sal_uInt16 nPos = _GetNamespaceMap().GetIndexByKey( XML_NAMESPACE_OFFICE );
    AddAttribute ( XML_NAMESPACE_NONE, _GetNamespaceMap().GetAttrNameByIndex ( nPos ),
                              _GetNamespaceMap().GetNameByIndex ( nPos ) );
    nPos = _GetNamespaceMap().GetIndexByKey( XML_NAMESPACE_TEXT );
    AddAttribute ( XML_NAMESPACE_NONE, _GetNamespaceMap().GetAttrNameByIndex ( nPos ),
                              _GetNamespaceMap().GetNameByIndex ( nPos ) );
    {
        SvXMLElementExport aDocument (*this, XML_NAMESPACE_OFFICE, sXML_document, sal_True, sal_True);
        {
            SvXMLElementExport aBody (*this, XML_NAMESPACE_OFFICE, sXML_body, sal_True, sal_True);
            {
                xub_StrLen nPos = 0;
                do
                {
                    String sTemp ( rText.GetToken( 0, '\015', nPos ) );
                     SvXMLElementExport aPara (*this, XML_NAMESPACE_TEXT, sXML_p, sal_True, sal_False);
                    GetDocHandler()->characters(sTemp);
                } while (STRING_NOTFOUND != nPos );
            }

        }
    }
    GetDocHandler()->endDocument();
    return 0;
}
<|endoftext|>


<commit_before>#include <assert.h>
#include <iostream>
#include <string>
#include "Creature.h"
#include "dLog.h"
#include "File.h"
#include "Inventory.h"
#include "utils.h"

Creature::Creature(): name("Creature")
  , inventory(Inventory{"Inv", 0})
  , isLiving(false) {
  dLog << "Creature ctor called" << std::endl;
}

Creature::~Creature() {
  dLog << "Creature dtor called" << std::endl;
}

void Creature::onDamage(int dmg) { // Should return damage taken
  // TODO: Update this for armor
  // modify dmg value based on armor
  if ( getIsLiving() ) {
    std::cout << getName() << " is hit for " << dmg << ". ";
    flagInCombat(true);
  }
  health -= dmg;
  if ( health <= 0 ) {
    health = 0;
    isLiving = false;
  }
  if (!getIsLiving()) {
    std::cout << getName() << " has been killed. ";
  }
  std::cout << std::endl;
}

void Creature::onHeal(int heal) {
  assert(heal >= 0);
  // TODO: Modify heal with abilities/ items
  if (getIsLiving()) {
    health += heal;
    if (health > maxHealth) {
      health = maxHealth;
    }
  }
  std::cout << getName() << " is healed for " << heal << ". " << std::endl;
}

void Creature::setHealth(int health) {
  this->health = health;
  isLiving = health > 0;
}

void Creature::setMaxHealth(int maxHealth) {
  this->maxHealth = maxHealth;
  setHealth(maxHealth);
}

void Creature::combatStop() {
  flagInCombat(false);
}

void Creature::flagInCombat(bool val) {
  isInCombat = val;
}

void Creature::setName(std::string name) {
  this->name = name;
}

void Creature::kill() {
  onDamage( health );
}

void Creature::useItem(Item &item, Creature &usedOn) {
  // TODO: Implement
  item.use(*this, usedOn);
}

void Creature::displayHUDLine() {
  if (!isLiving) {
    std::cout << "<Dead> ";
  }
  std::cout << name << ": " << health << "/"
    << maxHealth;
}

pairType Creature::toXML() {
  using namespace boost::property_tree;
  ptree tree;

  tree.push_back(XML_VAR_SPAIR(name));
  tree.push_back(XML_VAR_PAIR(isLiving));
  tree.push_back(XML_VAR_PAIR(isInCombat));

  tree.push_back(XML_VAR_PAIR(level));
  tree.push_back(XML_VAR_PAIR(experience));

  tree.push_back(XML_VAR_PAIR(health));
  tree.push_back(XML_VAR_PAIR(maxHealth));

  tree.push_back(equipment.toXML());
  tree.push_back(inventory.toXML());

  tree.push_back(stats.toXML());

  return pairType("Creature", tree);
}

void Creature::fromXML(const pairType& p) {
  const treeType &tree = p.second;
  auto it = tree.begin();

  while (it != tree.end()) {
    const std::string &key = it->first;
    const std::string &data = it->second.data();

    if (key == "name") {
      name = data;
    } else if (key == "isLiving") {
      isLiving = (bool)std::stoi(data);
    } else if (key == "isInCombat") {
      isInCombat = (bool)std::stoi(data);
    } else if (key == "level") {
      level = std::stoi(data);
    } else if (key == "experience") {
      experience = std::stoi(data);
    } else if (key == "health") {
      health = std::stoi(data);
    } else if (key == "maxHealth") {
      maxHealth = std::stoi(data);
    } else if (key == "Equipment") {
      equipment.fromXML(*it);
    } else if (key == "Inventory") {
      inventory.fromXML(*it);
    } else if (key == "Stats") {
      stats.fromXML(*it);
    }
    it++;
  }

}
<commit_msg>Creature update<commit_after>#include <assert.h>
#include <iostream>
#include <string>
#include "Creature.h"
#include "dLog.h"
#include "File.h"
#include "Inventory.h"
#include "utils.h"

Creature::Creature(): name("Creature")
  , inventory(Inventory{"Inv", 0})
  , isLiving(false) {
  dLog << "Creature ctor called" << std::endl;
}

Creature::~Creature() {
  dLog << "Creature dtor called" << std::endl;
}

void Creature::onDamage(int dmg) { // Should return damage taken
  // TODO: Update this for armor
  // modify dmg value based on armor
  if ( getIsLiving() ) {
    std::cout << getName() << " is hit for " << dmg << ". ";
    flagInCombat(true);
  }
  health -= dmg;
  if ( health <= 0 ) {
    health = 0;
    isLiving = false;
  }
  if (!getIsLiving()) {
    std::cout << getName() << " has been killed. ";
  }
  std::cout << std::endl;
}

void Creature::onHeal(int heal) {
  assert(heal >= 0);
  // TODO: Modify heal with abilities/ items
  if (getIsLiving()) {
    health += heal;
    if (health > maxHealth) {
      health = maxHealth;
    }
  }
  std::cout << getName() << " is healed for " << heal << ". " << std::endl;
}

void Creature::setHealth(int health) {
  this->health = health;
  isLiving = health > 0;
}

void Creature::setMaxHealth(int maxHealth) {
  this->maxHealth = maxHealth;
  setHealth(maxHealth);
}

void Creature::setIsLiving(bool isLiving)
{
  this->isLiving = isLiving;
}

void Creature::combatStop() {
  flagInCombat(false);
}

void Creature::flagInCombat(bool val) {
  isInCombat = val;
}

void Creature::setName(std::string name) {
  this->name = name;
}

void Creature::kill() {
  onDamage( health );
}

void Creature::useItem(Item &item, Creature &usedOn) {
  // TODO: Implement
  item.use(*this, usedOn);
}

void Creature::displayHUDLine() {
  if (!isLiving) {
    std::cout << "<Dead> ";
  }
  std::cout << name << ": " << health << "/"
    << maxHealth;
}

pairType Creature::toTree() {
  using namespace boost::property_tree;
  ptree tree;

  tree.push_back(XML_VAR_SPAIR(name));
  tree.push_back(XML_VAR_PAIR(isLiving));
  tree.push_back(XML_VAR_PAIR(isInCombat));

  tree.push_back(XML_VAR_PAIR(level));
  tree.push_back(XML_VAR_PAIR(experience));

  tree.push_back(XML_VAR_PAIR(health));
  tree.push_back(XML_VAR_PAIR(maxHealth));

  tree.push_back(equipment.toTree());
  tree.push_back(inventory.toTree());

  tree.push_back(stats.toTree());

  return pairType("Creature", tree);
}

void Creature::fromTree(const pairType& p) {
  const treeType &tree = p.second;
  auto it = tree.begin();

  while (it != tree.end()) {
    const std::string &key = it->first;
    const std::string &data = it->second.data();

    if (key == "name") {
      name = data;
    } else if (key == "isLiving") {
      isLiving = std::stoi(data) > 0;
    } else if (key == "isInCombat") {
      isInCombat = std::stoi(data) > 0;
    } else if (key == "level") {
      level = std::stoi(data);
    } else if (key == "experience") {
      experience = std::stoi(data);
    } else if (key == "health") {
      health = std::stoi(data);
    } else if (key == "maxHealth") {
      maxHealth = std::stoi(data);
    } else if (key == "Equipment") {
      equipment.fromTree(*it);
    } else if (key == "Inventory") {
      inventory.fromTree(*it);
    } else if (key == "Stats") {
      stats.fromTree(*it);
    }
    it++;
  }

}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <cstdio>
#include <string>

#include "base/at_exit.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "jingle/notifier/base/notification_method.h"
#include "jingle/notifier/base/notifier_options.h"
#include "net/base/host_port_pair.h"
#include "net/url_request/url_request_test_util.h"
#include "sync/notifier/invalidation_version_tracker.h"
#include "sync/notifier/sync_notifier.h"
#include "sync/notifier/sync_notifier_factory.h"
#include "sync/notifier/sync_notifier_observer.h"
#include "sync/syncable/model_type.h"
#include "sync/syncable/model_type_payload_map.h"

// This is a simple utility that initializes a sync notifier and
// listens to any received notifications.

namespace {

// Class to print received notifications events.
class NotificationPrinter : public sync_notifier::SyncNotifierObserver {
 public:
  NotificationPrinter() {}
  virtual ~NotificationPrinter() {}

  virtual void OnIncomingNotification(
      const syncable::ModelTypePayloadMap& type_payloads,
      sync_notifier::IncomingNotificationSource source) OVERRIDE {
    for (syncable::ModelTypePayloadMap::const_iterator it =
             type_payloads.begin(); it != type_payloads.end(); ++it) {
      LOG(INFO) << (source == sync_notifier::REMOTE_NOTIFICATION ?
                    "Remote" : "Local")
                << " Notification: type = "
                << syncable::ModelTypeToString(it->first)
                << ", payload = " << it->second;
    }
  }

  virtual void OnNotificationStateChange(
      bool notifications_enabled) OVERRIDE {
    LOG(INFO) << "Notifications enabled: " << notifications_enabled;
  }

  virtual void StoreState(const std::string& state) OVERRIDE {
    std::string base64_state;
    CHECK(base::Base64Encode(state, &base64_state));
    LOG(INFO) << "Got state to store: " << base64_state;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(NotificationPrinter);
};

class NullInvalidationVersionTracker
    : public base::SupportsWeakPtr<NullInvalidationVersionTracker>,
      public sync_notifier::InvalidationVersionTracker {
 public:
  NullInvalidationVersionTracker() {}
  virtual ~NullInvalidationVersionTracker() {}

  virtual sync_notifier::InvalidationVersionMap
      GetAllMaxVersions() const OVERRIDE {
    return sync_notifier::InvalidationVersionMap();
  }

  virtual void SetMaxVersion(
      syncable::ModelType model_type,
      int64 max_invalidation_version) OVERRIDE {
    LOG(INFO) << "Setting max invalidation version for "
              << syncable::ModelTypeToString(model_type) << " to "
              << max_invalidation_version;
  }
};

const char kEmailSwitch[] = "email";
const char kTokenSwitch[] = "token";
const char kHostPortSwitch[] = "host-port";
const char kTrySslTcpFirstSwitch[] = "try-ssltcp-first";
const char kAllowInsecureConnectionSwitch[] = "allow-insecure-connection";
const char kNotificationMethodSwitch[] = "notification-method";

notifier::NotifierOptions ParseNotifierOptions(
    const CommandLine& command_line,
    const scoped_refptr<net::URLRequestContextGetter>&
        request_context_getter) {
  notifier::NotifierOptions notifier_options;
  notifier_options.request_context_getter = request_context_getter;

  if (command_line.HasSwitch(kHostPortSwitch)) {
    notifier_options.xmpp_host_port =
        net::HostPortPair::FromString(
            command_line.GetSwitchValueASCII(kHostPortSwitch));
    LOG(INFO) << "Using " << notifier_options.xmpp_host_port.ToString()
              << " for test sync notification server.";
  }

  notifier_options.try_ssltcp_first =
      command_line.HasSwitch(kTrySslTcpFirstSwitch);
  LOG_IF(INFO, notifier_options.try_ssltcp_first)
      << "Trying SSL/TCP port before XMPP port for notifications.";

  notifier_options.allow_insecure_connection =
      command_line.HasSwitch(kAllowInsecureConnectionSwitch);
  LOG_IF(INFO, notifier_options.allow_insecure_connection)
      << "Allowing insecure XMPP connections.";

  if (command_line.HasSwitch(kNotificationMethodSwitch)) {
    notifier_options.notification_method =
        notifier::StringToNotificationMethod(
            command_line.GetSwitchValueASCII(kNotificationMethodSwitch));
  }

  return notifier_options;
}

}  // namespace

int main(int argc, char* argv[]) {
  base::AtExitManager exit_manager;
  CommandLine::Init(argc, argv);
  logging::InitLogging(
      NULL,
      logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
      logging::LOCK_LOG_FILE,
      logging::DELETE_OLD_LOG_FILE,
      logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);

  MessageLoop ui_loop;
  base::Thread io_thread("IO thread");
  base::Thread::Options options;
  options.message_loop_type = MessageLoop::TYPE_IO;
  io_thread.StartWithOptions(options);

  // Parse command line.
  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  std::string email = command_line.GetSwitchValueASCII(kEmailSwitch);
  std::string token = command_line.GetSwitchValueASCII(kTokenSwitch);
  // TODO(akalin): Write a wrapper script that gets a token for an
  // email and password and passes that in to this utility.
  if (email.empty() || token.empty()) {
    std::printf("Usage: %s --%s=foo@bar.com --%s=token\n"
                "[--%s=host:port] [--%s] [--%s]\n"
                "[--%s=(server|p2p)]\n\n"
                "Run chrome and set a breakpoint on\n"
                "sync_api::SyncManager::SyncInternal::UpdateCredentials() "
                "after logging into\n"
                "sync to get the token to pass into this utility.\n",
                argv[0],
                kTokenSwitch, kEmailSwitch, kHostPortSwitch,
                kTrySslTcpFirstSwitch, kAllowInsecureConnectionSwitch,
                kNotificationMethodSwitch);
    return -1;
  }

  const notifier::NotifierOptions& notifier_options =
      ParseNotifierOptions(
          command_line,
          new TestURLRequestContextGetter(io_thread.message_loop_proxy()));
  const char kClientInfo[] = "sync_listen_notifications";
  NullInvalidationVersionTracker null_invalidation_version_tracker;
  sync_notifier::SyncNotifierFactory sync_notifier_factory(
      notifier_options, kClientInfo,
      null_invalidation_version_tracker.AsWeakPtr());
  scoped_ptr<sync_notifier::SyncNotifier> sync_notifier(
      sync_notifier_factory.CreateSyncNotifier());
  NotificationPrinter notification_printer;
  sync_notifier->AddObserver(&notification_printer);

  const char kUniqueId[] = "fake_unique_id";
  sync_notifier->SetUniqueId(kUniqueId);
  sync_notifier->SetState("");
  sync_notifier->UpdateCredentials(email, token);
  // Listen for notifications for all known types.
  sync_notifier->UpdateEnabledTypes(syncable::ModelTypeSet::All());

  ui_loop.Run();

  sync_notifier->RemoveObserver(&notification_printer);
  io_thread.Stop();
  return 0;
}
<commit_msg>[Sync] Fix sync_listen_notification's usage message<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <cstdio>
#include <string>

#include "base/at_exit.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "jingle/notifier/base/notification_method.h"
#include "jingle/notifier/base/notifier_options.h"
#include "net/base/host_port_pair.h"
#include "net/url_request/url_request_test_util.h"
#include "sync/notifier/invalidation_version_tracker.h"
#include "sync/notifier/sync_notifier.h"
#include "sync/notifier/sync_notifier_factory.h"
#include "sync/notifier/sync_notifier_observer.h"
#include "sync/syncable/model_type.h"
#include "sync/syncable/model_type_payload_map.h"

// This is a simple utility that initializes a sync notifier and
// listens to any received notifications.

namespace {

// Class to print received notifications events.
class NotificationPrinter : public sync_notifier::SyncNotifierObserver {
 public:
  NotificationPrinter() {}
  virtual ~NotificationPrinter() {}

  virtual void OnIncomingNotification(
      const syncable::ModelTypePayloadMap& type_payloads,
      sync_notifier::IncomingNotificationSource source) OVERRIDE {
    for (syncable::ModelTypePayloadMap::const_iterator it =
             type_payloads.begin(); it != type_payloads.end(); ++it) {
      LOG(INFO) << (source == sync_notifier::REMOTE_NOTIFICATION ?
                    "Remote" : "Local")
                << " Notification: type = "
                << syncable::ModelTypeToString(it->first)
                << ", payload = " << it->second;
    }
  }

  virtual void OnNotificationStateChange(
      bool notifications_enabled) OVERRIDE {
    LOG(INFO) << "Notifications enabled: " << notifications_enabled;
  }

  virtual void StoreState(const std::string& state) OVERRIDE {
    std::string base64_state;
    CHECK(base::Base64Encode(state, &base64_state));
    LOG(INFO) << "Got state to store: " << base64_state;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(NotificationPrinter);
};

class NullInvalidationVersionTracker
    : public base::SupportsWeakPtr<NullInvalidationVersionTracker>,
      public sync_notifier::InvalidationVersionTracker {
 public:
  NullInvalidationVersionTracker() {}
  virtual ~NullInvalidationVersionTracker() {}

  virtual sync_notifier::InvalidationVersionMap
      GetAllMaxVersions() const OVERRIDE {
    return sync_notifier::InvalidationVersionMap();
  }

  virtual void SetMaxVersion(
      syncable::ModelType model_type,
      int64 max_invalidation_version) OVERRIDE {
    LOG(INFO) << "Setting max invalidation version for "
              << syncable::ModelTypeToString(model_type) << " to "
              << max_invalidation_version;
  }
};

const char kEmailSwitch[] = "email";
const char kTokenSwitch[] = "token";
const char kHostPortSwitch[] = "host-port";
const char kTrySslTcpFirstSwitch[] = "try-ssltcp-first";
const char kAllowInsecureConnectionSwitch[] = "allow-insecure-connection";
const char kNotificationMethodSwitch[] = "notification-method";

notifier::NotifierOptions ParseNotifierOptions(
    const CommandLine& command_line,
    const scoped_refptr<net::URLRequestContextGetter>&
        request_context_getter) {
  notifier::NotifierOptions notifier_options;
  notifier_options.request_context_getter = request_context_getter;

  if (command_line.HasSwitch(kHostPortSwitch)) {
    notifier_options.xmpp_host_port =
        net::HostPortPair::FromString(
            command_line.GetSwitchValueASCII(kHostPortSwitch));
    LOG(INFO) << "Using " << notifier_options.xmpp_host_port.ToString()
              << " for test sync notification server.";
  }

  notifier_options.try_ssltcp_first =
      command_line.HasSwitch(kTrySslTcpFirstSwitch);
  LOG_IF(INFO, notifier_options.try_ssltcp_first)
      << "Trying SSL/TCP port before XMPP port for notifications.";

  notifier_options.allow_insecure_connection =
      command_line.HasSwitch(kAllowInsecureConnectionSwitch);
  LOG_IF(INFO, notifier_options.allow_insecure_connection)
      << "Allowing insecure XMPP connections.";

  if (command_line.HasSwitch(kNotificationMethodSwitch)) {
    notifier_options.notification_method =
        notifier::StringToNotificationMethod(
            command_line.GetSwitchValueASCII(kNotificationMethodSwitch));
  }

  return notifier_options;
}

}  // namespace

int main(int argc, char* argv[]) {
  base::AtExitManager exit_manager;
  CommandLine::Init(argc, argv);
  logging::InitLogging(
      NULL,
      logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
      logging::LOCK_LOG_FILE,
      logging::DELETE_OLD_LOG_FILE,
      logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);

  MessageLoop ui_loop;
  base::Thread io_thread("IO thread");
  base::Thread::Options options;
  options.message_loop_type = MessageLoop::TYPE_IO;
  io_thread.StartWithOptions(options);

  // Parse command line.
  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
  std::string email = command_line.GetSwitchValueASCII(kEmailSwitch);
  std::string token = command_line.GetSwitchValueASCII(kTokenSwitch);
  // TODO(akalin): Write a wrapper script that gets a token for an
  // email and password and passes that in to this utility.
  if (email.empty() || token.empty()) {
    std::printf("Usage: %s --%s=foo@bar.com --%s=token\n"
                "[--%s=host:port] [--%s] [--%s]\n"
                "[--%s=(server|p2p)]\n\n"
                "Run chrome and set a breakpoint on\n"
                "sync_api::SyncManager::SyncInternal::UpdateCredentials() "
                "after logging into\n"
                "sync to get the token to pass into this utility.\n",
                argv[0],
                kEmailSwitch, kTokenSwitch, kHostPortSwitch,
                kTrySslTcpFirstSwitch, kAllowInsecureConnectionSwitch,
                kNotificationMethodSwitch);
    return -1;
  }

  const notifier::NotifierOptions& notifier_options =
      ParseNotifierOptions(
          command_line,
          new TestURLRequestContextGetter(io_thread.message_loop_proxy()));
  const char kClientInfo[] = "sync_listen_notifications";
  NullInvalidationVersionTracker null_invalidation_version_tracker;
  sync_notifier::SyncNotifierFactory sync_notifier_factory(
      notifier_options, kClientInfo,
      null_invalidation_version_tracker.AsWeakPtr());
  scoped_ptr<sync_notifier::SyncNotifier> sync_notifier(
      sync_notifier_factory.CreateSyncNotifier());
  NotificationPrinter notification_printer;
  sync_notifier->AddObserver(&notification_printer);

  const char kUniqueId[] = "fake_unique_id";
  sync_notifier->SetUniqueId(kUniqueId);
  sync_notifier->SetState("");
  sync_notifier->UpdateCredentials(email, token);
  // Listen for notifications for all known types.
  sync_notifier->UpdateEnabledTypes(syncable::ModelTypeSet::All());

  ui_loop.Run();

  sync_notifier->RemoveObserver(&notification_printer);
  io_thread.Stop();
  return 0;
}
<|endoftext|>


<commit_before><commit_msg>Unified file naming convention for manta structural variant files.<commit_after><|endoftext|>