-
-
Notifications
You must be signed in to change notification settings - Fork 886
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
769 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
// Copyright (c) 2023 UltiMaker | ||
// CuraEngine is released under the terms of the AGPLv3 or higher | ||
|
||
#ifndef UTILS_VIEWS_SMOOTH_H | ||
#define UTILS_VIEWS_SMOOTH_H | ||
|
||
#include <functional> | ||
#include <numbers> | ||
#include <set> | ||
|
||
#include <range/v3/action/remove_if.hpp> | ||
#include <range/v3/functional/bind_back.hpp> | ||
#include <range/v3/iterator/concepts.hpp> | ||
#include <range/v3/iterator/operations.hpp> | ||
#include <range/v3/range_fwd.hpp> | ||
#include <range/v3/view/addressof.hpp> | ||
#include <range/v3/view/concat.hpp> | ||
#include <range/v3/view/single.hpp> | ||
#include <range/v3/view/take.hpp> | ||
|
||
#include "utils/types/arachne.h" | ||
#include "utils/types/generic.h" | ||
#include "utils/types/geometry.h" | ||
#include "utils/types/get.h" | ||
|
||
namespace cura::actions | ||
{ | ||
|
||
struct smooth_fn | ||
{ | ||
constexpr auto operator()(const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const | ||
{ | ||
return ranges::make_action_closure(ranges::bind_back(smooth_fn{}, max_resolution, fluid_angle)); | ||
} | ||
|
||
template<class Rng> | ||
requires ranges::forward_range<Rng> && ranges::sized_range<Rng> && ranges::erasable_range<Rng, ranges::iterator_t<Rng>, ranges::sentinel_t<Rng>> && (utils::point2d<ranges::range_value_t<Rng>> || utils::junctions<Rng>) | ||
constexpr auto operator()(Rng&& rng, const utils::integral auto max_resolution, const utils::floating_point auto fluid_angle) const | ||
{ | ||
const auto size = ranges::distance(rng) - 1; | ||
if (size < 3) | ||
{ | ||
return static_cast<Rng&&>(rng); | ||
} | ||
|
||
using coord_type = std::remove_cvref_t<decltype(std::get<"X">(*ranges::begin(rng)))>; | ||
const auto allowed_deviation = static_cast<coord_type>(max_resolution * 2 / 3); // The allowed deviation from the original path | ||
const auto smooth_distance = static_cast<coord_type>(max_resolution / 2); // The distance over which the path is smoothed | ||
|
||
auto tmp = rng; // We don't want to shift the points of the in-going range, therefore we create a temporary copy | ||
auto windows = ranges::views::concat(ranges::views::single(ranges::back(tmp)), ranges::views::concat(tmp, tmp | ranges::views::take(4))) | ranges::views::addressof; | ||
|
||
// Smooth the path, by moving over three segments at a time. If the middle segment is shorter than the max resolution, then we try shifting those points outwards. | ||
// The previous and next segment should have a remaining length of at least the smooth distance, otherwise the point is not shifted, but deleted. | ||
for (auto windows_it = ranges::begin(windows); ranges::distance(windows_it, ranges::end(windows)) > 2; ++windows_it) | ||
{ | ||
auto A = *windows_it; | ||
auto B = *std::next(windows_it, 1); | ||
auto C = *std::next(windows_it, 2); | ||
auto D = *std::next(windows_it, 3); | ||
|
||
const auto [AB_magnitude, BC_magnitude, CD_magnitude] = computeMagnitudes(A, B, C, D); | ||
if (! isWithinAllowedDeviations(A, B, C, D, fluid_angle, max_resolution, AB_magnitude, BC_magnitude, CD_magnitude)) | ||
{ | ||
if (AB_magnitude > allowed_deviation) | ||
{ | ||
shiftPointTowards(B, A, AB_magnitude, smooth_distance); | ||
} | ||
if (CD_magnitude > allowed_deviation) | ||
{ | ||
shiftPointTowards(C, D, CD_magnitude, smooth_distance); | ||
} | ||
} | ||
} | ||
|
||
return tmp; | ||
} | ||
|
||
private: | ||
template<class Point> | ||
requires utils::point2d<Point> || utils::junction<Point> | ||
constexpr auto computeMagnitudes(Point* A, Point* B, Point* C, Point* D) const noexcept | ||
{ | ||
const auto AB_magnitude = std::hypot(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); | ||
const auto BC_magnitude = std::hypot(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); | ||
const auto CD_magnitude = std::hypot(std::get<"X">(*D) - std::get<"X">(*C), std::get<"Y">(*D) - std::get<"Y">(*C)); | ||
|
||
return std::make_tuple(AB_magnitude, BC_magnitude, CD_magnitude); | ||
} | ||
|
||
template<class Point> | ||
requires utils::point2d<Point> || utils::junction<Point> | ||
constexpr auto cosAngle(Point* A, Point* B, Point* C, const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude) const noexcept | ||
{ | ||
if (AB_magnitude == 0.0 || BC_magnitude == 0.0) | ||
{ | ||
return 0.0; | ||
} | ||
auto AB = std::make_tuple(std::get<"X">(*B) - std::get<"X">(*A), std::get<"Y">(*B) - std::get<"Y">(*A)); | ||
auto BC = std::make_tuple(std::get<"X">(*C) - std::get<"X">(*B), std::get<"Y">(*C) - std::get<"Y">(*B)); | ||
|
||
const auto dot = dotProduct(&AB, &BC); | ||
return dot / (AB_magnitude * BC_magnitude); | ||
} | ||
|
||
template<class Point> | ||
requires utils::point2d<Point> || utils::junction<Point> | ||
constexpr void shiftPointTowards(Point* point, Point* target, const utils::floating_point auto p0p1_distance, const utils::integral auto smooth_distance) const noexcept | ||
{ | ||
using coord_type = std::remove_cvref_t<decltype(std::get<"X">(*point))>; | ||
const auto shift_distance = smooth_distance / p0p1_distance; | ||
const auto shift_distance_x = static_cast<coord_type>((std::get<"X">(*target) - std::get<"X">(*point)) * shift_distance); | ||
const auto shift_distance_y = static_cast<coord_type>((std::get<"Y">(*target) - std::get<"Y">(*point)) * shift_distance); | ||
if constexpr (utils::point2d<Point>) | ||
{ | ||
point->X += shift_distance_x; | ||
point->Y += shift_distance_y; | ||
} | ||
else | ||
{ | ||
point->p.X += shift_distance_x; | ||
point->p.Y += shift_distance_y; | ||
} | ||
} | ||
|
||
template<class Vector> | ||
requires utils::point2d<Vector> || utils::junction<Vector> constexpr auto dotProduct(Vector* point_0, Vector* point_1) const noexcept | ||
{ | ||
return std::get<"X">(*point_0) * std::get<"X">(*point_1) + std::get<"Y">(*point_0) * std::get<"Y">(*point_1); | ||
} | ||
|
||
template<class Point> | ||
requires utils::point2d<Point> || utils::junction<Point> | ||
constexpr auto isWithinAllowedDeviations(Point* A, Point* B, Point* C, Point* D, const utils::floating_point auto fluid_angle, const utils::integral auto max_resolution, | ||
const utils::floating_point auto AB_magnitude, const utils::floating_point auto BC_magnitude, const utils::floating_point auto CD_magnitude) const noexcept | ||
{ | ||
if (BC_magnitude > max_resolution / 10) // TODO: make dedicated front-end setting for this | ||
{ | ||
return true; | ||
} | ||
const double cos_A = std::acos(cosAngle(A, B, C, AB_magnitude, BC_magnitude)); | ||
const double cos_B = std::acos(cosAngle(A, B, D, AB_magnitude, CD_magnitude)); | ||
const auto abs_angle = std::abs(cos_A - cos_B); | ||
return abs_angle < fluid_angle; | ||
} | ||
}; | ||
|
||
inline constexpr smooth_fn smooth{}; | ||
} // namespace cura::actions | ||
|
||
#endif // UTILS_VIEWS_SMOOTH_H |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// Copyright (c) 2023 UltiMaker | ||
// CuraEngine is released under the terms of the AGPLv3 or higher | ||
|
||
#ifndef UTILS_TYPES_ARACHNE_H | ||
#define UTILS_TYPES_ARACHNE_H | ||
|
||
#include <concepts> | ||
#include <string> | ||
#include <type_traits> | ||
|
||
#include <range/v3/range/concepts.hpp> | ||
|
||
#include "utils/types/generic.h" | ||
#include "utils/types/geometry.h" | ||
|
||
namespace cura::utils | ||
{ | ||
template<class T> | ||
concept st_storable_data = requires(T val) | ||
{ | ||
val.data; | ||
}; | ||
|
||
/*! | ||
* @brief A node in a skeleton trapezoidal graph, defined as a 2D point with additional stored data. | ||
* @details This concept is used to check if a type is a node in a straight skeleton graph. A node is a type that is a 2D point with additional stored data. | ||
* @tparam T Type to check | ||
*/ | ||
template<class T> | ||
concept st_node = requires(T val) | ||
{ | ||
requires point2d<decltype(val.p)>; | ||
}; | ||
|
||
/*! | ||
* @brief A edge in a skeleton trapezoidal graph, defined as a 2D point with additional stored data. | ||
* @details This concept is used to check if a type is a edge in a skeleton trapezoidal graph. defined as a pair of nodes with pointers to the next, previous, and twin edges, and additional stored data. | ||
* @tparam T Type to check | ||
*/ | ||
template<class T> | ||
concept st_edge = requires(T val) | ||
{ | ||
requires std::is_pointer_v<decltype(val.from)>; | ||
requires st_node<decltype(*val.from)>; | ||
requires std::is_pointer_v<decltype(val.to)>; | ||
requires st_node<decltype(*val.to)>; | ||
requires std::is_pointer_v<decltype(val.twin)>; | ||
requires std::is_pointer_v<decltype(val.next)>; | ||
requires std::is_pointer_v<decltype(val.prev)>; | ||
}; | ||
|
||
template<class T> | ||
concept st_edges = requires(T edges) | ||
{ | ||
requires ranges::range<T>; | ||
requires st_edge<decltype(*ranges::begin(edges))>; | ||
requires st_storable_data<decltype(*ranges::begin(edges))>; | ||
}; | ||
|
||
template<class T> | ||
concept st_nodes = requires(T nodes) | ||
{ | ||
requires ranges::range<T>; | ||
requires st_node<decltype(*ranges::begin(nodes))>; | ||
requires st_storable_data<decltype(*ranges::begin(nodes))>; | ||
}; | ||
|
||
/*! | ||
* @brief A skeleton trapezoidal graph, defined as a collection of nodes and edges. | ||
* @details This concept is used to check if a type is a skeleton trapezoidal graph. | ||
* @tparam T Type to check | ||
*/ | ||
template<class T> | ||
concept st_graph = requires(T graph) | ||
{ | ||
requires st_edges<decltype(graph.edges)>; | ||
requires st_nodes<decltype(graph.nodes)>; | ||
}; | ||
|
||
/*! | ||
* @brief A 2D point with an associated weight value | ||
* @details This concept is used to check if a type is a junction as used in wall toolpaths | ||
* @tparam T Type to check | ||
*/ | ||
template<class T> | ||
concept junction = requires(T val) | ||
{ | ||
requires point2d<decltype(val.p)>; | ||
requires utils::integral<decltype(val.w)>; | ||
}; | ||
|
||
/*! | ||
* @brief A collection of junctions | ||
* @details This concept is used to check if a type is a collection of junctions | ||
* @tparam T Type to check | ||
*/ | ||
template<class T> | ||
concept junctions = requires(T val) | ||
{ | ||
requires ranges::range<T>; | ||
requires junction<decltype(*ranges::begin(val))>; | ||
}; | ||
|
||
/*! | ||
* @brief an Extrusion line in a toolpath | ||
* @details This concept is used to check if a type is a collection of junctions. A series of junctions defining a path for extrusion, | ||
* with additional flags indicating whether the path is closed and whether it corresponds to an odd or even layer. | ||
* @tparam T Type to check | ||
*/ | ||
template<class T> | ||
concept extrusion_line = requires(T val) | ||
{ | ||
std::is_same_v<decltype(val.inset_idx), size_t>; | ||
std::is_same_v<decltype(val.is_odd), bool>; | ||
std::is_same_v<decltype(val.is_closed), bool>; | ||
requires junctions<decltype(val.junctions)>; | ||
}; | ||
|
||
/*! | ||
* @brief A collection of extrusion lines | ||
* @details This concept is used to check if a type is a collection of extrusion lines | ||
* @tparam T Type to check | ||
*/ | ||
template<class T> | ||
concept toolpath = requires(T tp) | ||
{ | ||
requires ranges::range<T>; | ||
requires extrusion_line<decltype(*ranges::begin(tp))>; | ||
}; | ||
|
||
/*! | ||
* @brief A collection of toolpaths | ||
* @details This concept is used to check if a type is a collection of toolpaths | ||
* @tparam T Type to check | ||
*/ | ||
template<class T> | ||
concept toolpaths = requires(T tp) | ||
{ | ||
requires ranges::range<T>; | ||
requires toolpath<decltype(*ranges::begin(tp))>; | ||
}; | ||
|
||
} // namespace cura::utils | ||
|
||
#endif // UTILS_TYPES_ARACHNE_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) 2023 UltiMaker | ||
// CuraEngine is released under the terms of the AGPLv3 or higher | ||
|
||
#ifndef UTILS_TYPES_CHAR_RANGE_LITERAL_H | ||
#define UTILS_TYPES_CHAR_RANGE_LITERAL_H | ||
|
||
#include <algorithm> | ||
|
||
|
||
namespace cura::utils | ||
{ | ||
template<size_t N> | ||
struct CharRangeLiteral | ||
{ | ||
constexpr CharRangeLiteral(const char (&str)[N]) | ||
{ | ||
std::copy_n(str, N, value); | ||
} | ||
|
||
char value[N]; | ||
}; | ||
|
||
} // namespace cura::utils | ||
|
||
#endif // UTILS_TYPES_CHAR_RANGE_LITERAL_H |
Oops, something went wrong.