Skip to content

Commit

Permalink
Merge branch '5.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
jellespijker committed Jul 4, 2023
2 parents 03d93b6 + 54e6180 commit 4a80185
Show file tree
Hide file tree
Showing 19 changed files with 769 additions and 48 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ option(ENABLE_TESTING "Build with unit tests" OFF)
option(EXTENSIVE_WARNINGS "Build with all warnings" ON)
option(ENABLE_MORE_COMPILER_OPTIMIZATION_FLAGS "Enable more optimization flags" ON)
option(USE_SYSTEM_LIBS "Use the system libraries if available" OFF)
option(RETARDED_APPLE_CLANG "Apple Clang <= 13 used" OFF)

# Create Protobuf files if Arcus is used
if (ENABLE_ARCUS)
Expand Down Expand Up @@ -149,6 +150,7 @@ target_include_directories(_CuraEngine
target_compile_definitions(_CuraEngine
PUBLIC
$<$<BOOL:${ENABLE_ARCUS}>:ARCUS>
$<$<BOOL:${OLDER_APPLE_CLANG}>:OLDER_APPLE_CLANG>
CURA_ENGINE_VERSION=\"${CURA_ENGINE_VERSION}\"
$<$<BOOL:${ENABLE_TESTING}>:BUILD_TESTS>
PRIVATE
Expand Down
1 change: 1 addition & 0 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def generate(self):
tc.variables["ENABLE_TESTING"] = self.options.enable_testing
tc.variables["ENABLE_BENCHMARKS"] = self.options.enable_benchmarks
tc.variables["EXTENSIVE_WARNINGS"] = self.options.enable_extensive_warnings
tc.variables["OLDER_APPLE_CLANG"] = self.settings.compiler == "apple-clang" and Version(self.settings.compiler.version) < "14"
tc.generate()

for dep in self.dependencies.values():
Expand Down
151 changes: 151 additions & 0 deletions include/utils/actions/smooth.h
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
19 changes: 0 additions & 19 deletions include/utils/concepts/generic.h

This file was deleted.

145 changes: 145 additions & 0 deletions include/utils/types/arachne.h
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
25 changes: 25 additions & 0 deletions include/utils/types/char_range_literal.h
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
Loading

0 comments on commit 4a80185

Please sign in to comment.