Skip to content

Commit

Permalink
Use a multi-pass scoring to better handle seams on regular shapes
Browse files Browse the repository at this point in the history
CURA-12218
The previous rework of the score calculation removed the relative
comparison between vertices scores to ensure the consistency. The new
strategy performed very badly on shapes where multiple points would get
a similar score, e.g. finding sharp corners on a cylinder. We now
process a multi-pass scoring, so that all points having a very similar
score at their main criterion will now go through a second pass with
different criteria.
  • Loading branch information
wawanbreton committed Oct 29, 2024
1 parent d15d273 commit 81d2ce0
Show file tree
Hide file tree
Showing 15 changed files with 604 additions and 310 deletions.
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ set(engine_SRCS # Except main.cpp.
src/utils/VoxelUtils.cpp
src/utils/MixedPolylineStitcher.cpp

src/utils/scoring/BestElementFinder.cpp
src/utils/scoring/CornerScoringCriterion.cpp
src/utils/scoring/DistanceScoringCriterion.cpp
src/utils/scoring/ExclusionAreaScoringCriterion.cpp
src/utils/scoring/RandomScoringCriterion.cpp

src/geometry/Point2LL.cpp
src/geometry/Point3LL.cpp
src/geometry/Polygon.cpp
Expand Down
270 changes: 52 additions & 218 deletions include/PathOrderOptimizer.h

Large diffs are not rendered by default.

31 changes: 0 additions & 31 deletions include/utils/CriterionScore.h

This file was deleted.

61 changes: 0 additions & 61 deletions include/utils/Score.h

This file was deleted.

81 changes: 81 additions & 0 deletions include/utils/scoring/BestElementFinder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2024 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef UTILS_SCORING_BESTCANDIDATEFINDER_H
#define UTILS_SCORING_BESTCANDIDATEFINDER_H

#include <algorithm>
#include <memory>
#include <optional>
#include <vector>

namespace cura
{
class ScoringCriterion;

/*!
* This class implements an algorithm to find an element amongst a list, regarding one or multiple scoring criteria. The
* criteria are implemented by subclassing the CriterionScoring class. It is also possible to setup multiple passes of
* criteria. Thus, if the first pass gives a few best results that are too close to each other, a new pass is processed
* with different criteria, and so on until we have a single outsider or no more criteria.
*/
class BestElementFinder
{
private:
/*!
* Contains the index of an element in the source list, and its calculated score
*/
struct Candidate
{
size_t candidate_index;
double score{ 0.0 };
};

public:
/*!
* Contains a criterion to be processed to calculate the score of an element, and the weight is has on the global
* score calculation.
*/
struct WeighedCriterion
{
std::shared_ptr<ScoringCriterion> criterion;

/*!
* The weight to be given when taking this criterion into the global score. A score that contributes "normally"
* to the global score should have a weight of 1.0, and others should be adjusted around this value, to give
* them more or less influence.
*/
double weight{ 1.0 };
};

/*!
* Contains multiple criteria to be processed on each element in a single pass
*/
struct CriteriaPass
{
std::vector<WeighedCriterion> criteria;

/*!
* Once we have calculated the global scores of each element for this pass, we calculate the score difference
* between the best candidate and the following ones. If the following ones have a score close enough to the
* best, within this threshold, they will also be considered outsiders and will be run for the next pass.
*/
double outsider_delta_threshold{ 0.0 };
};

private:
std::vector<CriteriaPass> criteria_;

public:
explicit BestElementFinder() = default;

void appendCriteriaPass(const CriteriaPass& pass)
{
criteria_.push_back(pass);
}

std::optional<size_t> findBestElement(const size_t candidates_count);
};

} // namespace cura
#endif // UTILS_SCORING_BESTCANDIDATEFINDER_H
67 changes: 67 additions & 0 deletions include/utils/scoring/CornerScoringCriterion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef CORNERSCORINGCRITERION_H
#define CORNERSCORINGCRITERION_H

#include <stddef.h>

#include "geometry/Point2LL.h"
#include "settings/EnumSettings.h"
#include "utils/Coord_t.h"
#include "utils/scoring/ScoringCriterion.h"

namespace cura
{
class PointsSet;

/*!
* Criterion that will give a score according to whether the point is creating a corner or lies on a flat line.
* Depending on the given preference, concave or convex corners may get a higher score.
*/
class CornerScoringCriterion : public ScoringCriterion
{
private:
const PointsSet& points_;
const EZSeamCornerPrefType corner_preference_;
std::vector<coord_t> segments_sizes_;
coord_t total_length_{ 0 };

public:
explicit CornerScoringCriterion(const PointsSet& points, const EZSeamCornerPrefType corner_preference);

virtual double computeScore(const size_t candidate_index) const override;

private:
/*!
* Some models have very sharp corners, but also have a high resolution. If a sharp corner
* consists of many points each point individual might have a shallow corner, but the
* collective angle of all nearby points is greater. To counter this the cornerAngle is
* calculated from two points within angle_query_distance of the query point, no matter
* what segment this leads us to
* \param vertex_index index of the query point
* \param angle_query_distance query range (default to 1mm)
* \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0]
*/
double cornerAngle(size_t vertex_index, const coord_t angle_query_distance = 1000) const;

/*!
* Finds a neighbour point on the path, located before or after the given reference point. The neighbour point
* is computed by travelling on the path and stopping when the distance has been reached, For example:
* |------|---------|------|--------------*---|
* H A B C N D
* In this case, H is the start point of the path and ABCD are the actual following points of the path.
* The neighbour point N is found by reaching point D then going a bit backward on the previous segment.
* This approach gets rid of the mesh actual resolution and gives a neighbour point that is on the path
* at a given physical distance.
* \param vertex_index The starting point index
* \param distance The distance we want to travel on the path, which may be positive to go forward
* or negative to go backward
* \return The position of the path a the given distance from the reference point
*/
Point2LL findNeighbourPoint(size_t vertex_index, coord_t distance) const;
};

} // namespace cura

#endif // CORNERSCORINGCRITERION_H
32 changes: 32 additions & 0 deletions include/utils/scoring/DistanceScoringCriterion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef DISTANCESCORINGCRITERION_H
#define DISTANCESCORINGCRITERION_H

#include "geometry/Point2LL.h"
#include "utils/scoring/ScoringCriterion.h"

namespace cura
{
class PointsSet;

/*!
* Criterion that will give a score according to the distance from the point to a target point. Closer points will get
* a higher score.
*/
class DistanceScoringCriterion : public ScoringCriterion
{
private:
const PointsSet& points_;
const Point2LL& target_pos_;

public:
explicit DistanceScoringCriterion(const PointsSet& points, const Point2LL& target_pos);

virtual double computeScore(const size_t candidate_index) const override;
};

} // namespace cura

#endif // DISTANCESCORINGCRITERION_H
34 changes: 34 additions & 0 deletions include/utils/scoring/ExclusionAreaScoringCriterion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef EXCLUSIONAREASCORINGCRITERION_H
#define EXCLUSIONAREASCORINGCRITERION_H

#include <stddef.h>

#include "utils/scoring/ScoringCriterion.h"

namespace cura
{
class PointsSet;
class Shape;

/*!
* Criterion that will give a score according to whether the point is located inside or outside of an exclusion area.
* This is currently a binary test and the score will be either 0 or 1.
*/
class ExclusionAreaScoringCriterion : public ScoringCriterion
{
private:
const PointsSet& points_;
const Shape& exclusion_area_;

public:
explicit ExclusionAreaScoringCriterion(const PointsSet& points, const Shape& exclusion_area);

virtual double computeScore(const size_t candidate_index) const override;
};

} // namespace cura

#endif // EXCLUSIONAREASCORINGCRITERION_H
27 changes: 27 additions & 0 deletions include/utils/scoring/RandomScoringCriterion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef RANDOMSCORINGCRITERION_H
#define RANDOMSCORINGCRITERION_H

#include <stddef.h>

#include "utils/scoring/ScoringCriterion.h"

namespace cura
{

/*!
* Criterion that will give a random score whatever the element is.
*/
class RandomScoringCriterion : public ScoringCriterion
{
public:
explicit RandomScoringCriterion();

virtual double computeScore(const size_t candidate_index) const override;
};

} // namespace cura

#endif // RANDOMSCORINGCRITERION_H
34 changes: 34 additions & 0 deletions include/utils/scoring/ScoringCriterion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2024 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef UTILS_SCORING_SCORINGCRITERION_H
#define UTILS_SCORING_SCORINGCRITERION_H

#include <stddef.h>

namespace cura
{

/*!
* Base class for implementing a selection criterion when calculating a multi-criteria score to select the best element
* amongst a list.
*/
class ScoringCriterion
{
public:
ScoringCriterion() = default;

virtual ~ScoringCriterion() = default;

/*!
* \brief Computes the score of an element regarding this criterion. To ensure a proper selection, this value must
* be contained in [0.0, 1.0] and the different given scores must be evenly distributed in this range.
* \param candidate_index The index of the candidate of the original list
* \return The raw score of the element regarding this criterion
*/
virtual double computeScore(const size_t candidate_index) const = 0;
};

} // namespace cura

#endif // UTILS_SCORING_SCORINGCRITERION_H
Loading

0 comments on commit 81d2ce0

Please sign in to comment.