Cell from over limit points

This commit is contained in:
Filip Sykala 2021-03-18 11:57:15 +01:00 committed by Lukas Matena
parent 81f0ad8f63
commit 9c9880aba8
13 changed files with 778 additions and 192 deletions

View File

@ -1,9 +1,9 @@
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Lukáš Hejl @hejllukas
///|/ Copyright (c) Prusa Research 2016 - 2023 Vojt<EFBFBD>ch Bubn<62>k @bubnikv, Enrico Turri @enricoturri1966, Tom<6F> M<>sz<73>ros @tamasmeszaros, Luk<75> Mat<61>na @lukasmatena, Filip Sykala @Jony01, Luk<75> Hejl @hejllukas
///|/ Copyright (c) 2017 Eyal Soha @eyal0
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
///|/
///|/ ported from lib/Slic3r/Geometry.pm:
///|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv
///|/ Copyright (c) Prusa Research 2017 - 2022 Vojt<EFBFBD>ch Bubn<62>k @bubnikv
///|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel
///|/ Copyright (c) 2013 Jose Luis Perez Diez
///|/ Copyright (c) 2013 Anders Sundman
@ -309,6 +309,39 @@ bool liang_barsky_line_clipping(
return liang_barsky_line_clipping(x0clip, x1clip, bbox);
}
// Ugly named variant, that accepts the squared line
// Don't call me with a nearly zero length vector!
template<typename T>
int ray_circle_intersections_r2_lv2_c(T r2, T a, T b, T lv2, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
{
T x0 = - a * c / lv2;
T y0 = - b * c / lv2;
T d = r2 - c * c / lv2;
if (d < T(0))
return 0;
T mult = sqrt(d / lv2);
out.first.x() = x0 + b * mult;
out.first.y() = y0 - a * mult;
out.second.x() = x0 - b * mult;
out.second.y() = y0 + a * mult;
return mult == T(0) ? 1 : 2;
}
template<typename T>
int ray_circle_intersections(T r, T a, T b, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
{
T lv2 = a * a + b * b;
if (lv2 < T(SCALED_EPSILON * SCALED_EPSILON)) {
//FIXME what is the correct epsilon?
// What if the line touches the circle?
return false;
}
return ray_circle_intersections_r2_lv2_c(r * r, a, b, a * a + b * b, c, out);
}
Pointf3s convex_hull(Pointf3s points);
Polygon convex_hull(Points points);
Polygon convex_hull(const Polygons &polygons);
bool directions_parallel(double angle1, double angle2, double max_diff = 0);
bool directions_perpendicular(double angle1, double angle2, double max_diff = 0);
template<class T> bool contains(const std::vector<T> &vector, const Point &point);

View File

@ -1,4 +1,6 @@
#include "LineUtils.hpp"
#include <libslic3r/Geometry.hpp>
#include <functional>
using namespace Slic3r::sla;
@ -18,3 +20,212 @@ void LineUtils::sort_CCW(Lines &lines, const Point& center)
}
});
}
bool LineUtils::is_parallel_y(const Line &line) {
coord_t x_change = line.a.x() - line.b.x();
return (x_change == 0);
}
bool LineUtils::is_parallel_y(const Linef &line)
{
double x_change = line.a.x() - line.b.x();
return (fabs(x_change) < std::numeric_limits<double>::epsilon());
}
std::optional<Slic3r::Line> LineUtils::crop_ray(const Line & ray,
const Point &center,
double radius)
{
if (is_parallel_y(ray)) {
coord_t x = ray.a.x();
coord_t diff = x - center.x();
coord_t abs_diff = abs(diff);
if (abs_diff > radius) return {};
// create cross points
double move_y = sqrt(radius * radius - (double) x * x);
coord_t y = std::round(move_y);
Point first(x, y);
Point second(x,-y);
return Line(first + center, second + center);
} else {
Line moved_line(ray.a - center, ray.b - center);
double a, b, c;
std::tie(a, b, c) = get_param(moved_line);
std::pair<Vec2d, Vec2d> points;
int count = Slic3r::Geometry::ray_circle_intersections(
radius, a, b, c, points);
if (count != 2) return {};
return Line(points.first.cast<coord_t>() + center,
points.second.cast<coord_t>() + center);
}
}
std::optional<Slic3r::Linef> LineUtils::crop_ray(const Linef &ray,
const Point &center,
double radius)
{
Vec2d center_d = center.cast<double>();
if (is_parallel_y(ray)) {
double x = ray.a.x();
double diff = x - center.x();
double abs_diff = fabs(diff);
if (abs_diff > radius) return {};
// create cross points
double y = sqrt(radius * radius - x * x);
Vec2d first(x, y);
Vec2d second(x, -y);
return Linef(first + center_d,
second + center_d);
} else {
Linef moved_line(ray.a - center_d, ray.b - center_d);
double a, b, c;
std::tie(a, b, c) = get_param(moved_line);
std::pair<Vec2d, Vec2d> points;
int count = Slic3r::Geometry::ray_circle_intersections(radius, a, b,
c, points);
if (count != 2) return {};
return Linef(points.first + center_d, points.second + center_d);
}
}
std::optional<Slic3r::Line> LineUtils::crop_half_ray(const Line & half_ray,
const Point &center,
double radius)
{
auto segment = crop_ray(half_ray, center, radius);
if (!segment.has_value()) return {};
Point dir = half_ray.b - half_ray.a;
using fnc = std::function<bool(const Point &)>;
fnc use_point_x = [&half_ray, &dir](const Point &p) -> bool {
return (p.x() > half_ray.a.x()) == (dir.x() > 0);
};
fnc use_point_y = [&half_ray, &dir](const Point &p) -> bool {
return (p.y() > half_ray.a.y()) == (dir.y() > 0);
};
bool use_x = abs(dir.x()) > abs(dir.y());
fnc use_point = (use_x) ? use_point_x : use_point_y;
bool use_a = use_point(segment->a);
bool use_b = use_point(segment->b);
if (!use_a && !use_b) return {};
if (use_a && use_b) return segment;
return Line(half_ray.a, (use_a)?segment->a : segment->b);
}
std::optional<Slic3r::Linef> LineUtils::crop_half_ray(const Linef & half_ray,
const Point &center,
double radius)
{
auto segment = crop_ray(half_ray, center, radius);
if (!segment.has_value()) return {};
Vec2d dir = half_ray.b - half_ray.a;
using fnc = std::function<bool(const Vec2d &)>;
fnc use_point_x = [&half_ray, &dir](const Vec2d &p) -> bool {
return (p.x() > half_ray.a.x()) == (dir.x() > 0);
};
fnc use_point_y = [&half_ray, &dir](const Vec2d &p) -> bool {
return (p.y() > half_ray.a.y()) == (dir.y() > 0);
};
bool use_x = fabs(dir.x()) > fabs(dir.y());
fnc use_point = (use_x) ? use_point_x : use_point_y;
bool use_a = use_point(segment->a);
bool use_b = use_point(segment->b);
if (!use_a && !use_b) return {};
if (use_a && use_b) return segment;
return Linef(half_ray.a, (use_a) ? segment->a : segment->b);
}
std::optional<Slic3r::Line> LineUtils::crop_line(const Line & line,
const Point &center,
double radius)
{
auto segment = crop_ray(line, center, radius);
if (!segment.has_value()) return {};
Point dir = line.b - line.a;
using fnc = std::function<bool(const Point &)>;
fnc use_point_x = [&line, &dir](const Point &p) -> bool {
return (dir.x() > 0) ? (p.x() > line.a.x()) && (p.x() < line.b.x()) :
(p.x() < line.a.x()) && (p.x() > line.b.x());
};
fnc use_point_y = [&line, &dir](const Point &p) -> bool {
return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) :
(p.y() < line.a.y()) && (p.y() > line.b.y());
};
bool use_x = abs(dir.x()) > abs(dir.y());
fnc use_point = (use_x) ? use_point_x : use_point_y;
bool use_a = use_point(segment->a);
bool use_b = use_point(segment->b);
if (!use_a && !use_b) return {};
if (use_a && use_b) return segment;
bool same_dir = (use_x) ?
((dir.x() > 0) == (segment->b.x() - segment->a.x()) > 0) :
((dir.y() > 0) == (segment->b.y() - segment->a.y()) > 0) ;
if (use_a) {
if (same_dir)
return Line(segment->a, line.b);
else
return Line(line.a, segment->a);
} else { // use b
if (same_dir)
return Line(line.a, segment->b);
else
return Line(segment->b, line.b);
}
}
std::optional<Slic3r::Linef> LineUtils::crop_line(const Linef & line,
const Point &center,
double radius)
{
auto segment = crop_ray(line, center, radius);
if (!segment.has_value()) return {};
Vec2d dir = line.b - line.a;
using fnc = std::function<bool(const Vec2d &)>;
fnc use_point_x = [&line, &dir](const Vec2d &p) -> bool {
return (dir.x() > 0) ? (p.x() > line.a.x()) && (p.x() < line.b.x()) :
(p.x() < line.a.x()) && (p.x() > line.b.x());
};
fnc use_point_y = [&line, &dir](const Vec2d &p) -> bool {
return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) :
(p.y() < line.a.y()) && (p.y() > line.b.y());
};
bool use_x = abs(dir.x()) > abs(dir.y());
fnc use_point = (use_x) ? use_point_x : use_point_y;
bool use_a = use_point(segment->a);
bool use_b = use_point(segment->b);
if (!use_a && !use_b) return {};
if (use_a && use_b) return segment;
bool same_dir = (use_x) ? ((dir.x() > 0) ==
(segment->b.x() - segment->a.x()) > 0) :
((dir.y() > 0) ==
(segment->b.y() - segment->a.y()) > 0);
if (use_a) {
if (same_dir)
return Linef(segment->a, line.b);
else
return Linef(line.a, segment->a);
} else { // use b
if (same_dir)
return Linef(line.a, segment->b);
else
return Linef(segment->b, line.b);
}
}
std::tuple<double, double, double> LineUtils::get_param(const Line &line) {
Vector normal = line.normal();
double a = normal.x();
double b = normal.y();
double c = -a * line.a.x() - b * line.a.y();
return {a, b, c};
}
std::tuple<double, double, double> LineUtils::get_param(const Linef &line)
{
Vec2d direction = line.b - line.a;
Vec2d normal(-direction.y(), direction.x());
double a = normal.x();
double b = normal.y();
double c = -a * line.a.x() - b * line.a.y();
return {a, b, c};
}

View File

@ -1,6 +1,8 @@
#ifndef slic3r_SLA_SuppotstIslands_LineUtils_hpp_
#define slic3r_SLA_SuppotstIslands_LineUtils_hpp_
#include <optional>
#include <tuple>
#include <libslic3r/Line.hpp>
namespace Slic3r::sla {
@ -21,6 +23,64 @@ public:
/// <param name="lines">Lines to sort</param>
/// <param name="center">Center for order</param>
static void sort_CCW(Lines &lines, const Point &center);
/// <summary>
/// Create line segment intersection of line and circle
/// </summary>
/// <param name="line">Input line.</param>
/// <param name="center">Circle center.</param>
/// <param name="radius">Circle radius.</param>
/// <returns>Chord -> line segment inside circle</returns>
static std::optional<Slic3r::Line> crop_line(const Line & line,
const Point &center,
double radius);
static std::optional<Slic3r::Linef> crop_line(const Linef & line,
const Point &center,
double radius);
/// <summary>
/// Create line segment intersection of ray and circle, when exist
/// </summary>
/// <param name="ray">Input ray.</param>
/// <param name="center">Circle center.</param>
/// <param name="radius">Circle radius.</param>
/// <returns>Chord -> line segment inside circle</returns>
static std::optional<Slic3r::Line> crop_ray(const Line & ray,
const Point &center,
double radius);
static std::optional<Slic3r::Linef> crop_ray(const Linef & ray,
const Point &center,
double radius);
/// <summary>
/// Create line segment intersection of half ray(start point and direction) and circle, when exist
/// </summary>
/// <param name="half_ray">Use Line::a as start point and Line::b as direction but no limit</param>
/// <param name="center">Circle center.</param>
/// <param name="radius">Circle radius.</param>
/// <returns>Chord -> line segment inside circle</returns>
static std::optional<Slic3r::Line> crop_half_ray(const Line & half_ray,
const Point &center,
double radius);
static std::optional<Slic3r::Linef> crop_half_ray(const Linef & half_ray,
const Point &center,
double radius);
/// <summary>
/// check if line is parallel to Y
/// </summary>
/// <param name="line">Input line</param>
/// <returns>True when parallel otherwise FALSE</returns>
static bool is_parallel_y(const Line &line);
static bool is_parallel_y(const Linef &line);
/// <summary>
/// Create parametric coeficient
/// ax + by + c = 0
/// Can't be parallel to Y axis - check by function is_parallel_y
/// </summary>
/// <param name="line">Input line - cant be parallel with y axis</param>
/// <returns>a, b, c</returns>
static std::tuple<double, double, double> get_param(const Line &line);
static std::tuple<double, double, double> get_param(const Linef &line);
};
} // namespace Slic3r::sla

View File

@ -0,0 +1,55 @@
#include "PolygonUtils.hpp"
using namespace Slic3r::sla;
Slic3r::Polygon PolygonUtils::create_regular(size_t count_points,
double radius,
const Point &center)
{
assert(radius >= 1.);
assert(count_points >= 3);
auto is_in_limits = [](double value) {
return (value < std::numeric_limits<coord_t>::max()) &&
(value > std::numeric_limits<coord_t>::min());
};
Points points;
points.reserve(count_points);
double increase_angle = 2 * M_PI / count_points;
for (int i = 0; i < count_points; ++i) {
double angle = i * increase_angle;
double x = cos(angle) * radius + center.x();
assert(is_in_limits(x));
double y = sin(angle) * radius + center.y();
assert(is_in_limits(y));
points.emplace_back(x, y);
}
return Polygon(points);
}
Slic3r::Polygon PolygonUtils::create_equilateral_triangle(double edge_size)
{
return {{.0, .0},
{edge_size, .0},
{edge_size / 2., sqrt(edge_size * edge_size - edge_size * edge_size / 4)}};
}
Slic3r::Polygon PolygonUtils::create_isosceles_triangle(double side, double height)
{
return {{-side / 2, 0.}, {side / 2, 0.}, {.0, height}};
}
Slic3r::Polygon PolygonUtils::create_square(double size)
{
double size_2 = size / 2;
return {{-size_2, size_2},
{-size_2, -size_2},
{size_2, -size_2},
{size_2, size_2}};
}
Slic3r::Polygon PolygonUtils::create_rect(double width, double height)
{
double x_2 = width / 2;
double y_2 = height / 2;
return {{-x_2, y_2}, {-x_2, -y_2}, {x_2, -y_2}, {x_2, y_2}};
}

View File

@ -0,0 +1,68 @@
#ifndef slic3r_SLA_SuppotstIslands_PolygonUtils_hpp_
#define slic3r_SLA_SuppotstIslands_PolygonUtils_hpp_
#include <libslic3r/Polygon.hpp>
namespace Slic3r::sla {
/// <summary>
/// Class which contain collection of static function
/// for work with Polygon.
/// </summary>
class PolygonUtils
{
public:
PolygonUtils() = delete;
/// <summary>
/// Create regular polygon with N points
/// </summary>
/// <param name="count_points">Count points of regular polygon</param>
/// <param name="radius">Radius around center</param>
/// <param name="center">Center point</param>
/// <returns>Regular Polygon with CCW points</returns>
static Polygon create_regular(size_t count_points, double radius = 10., const Point& center = Point(0,0));
/// <summary>
/// Create circle with N points
/// alias for create regular
/// </summary>
/// <param name="radius">Radius of circle</param>
/// <param name="count_points">Count points of circle</param>
/// <param name="center">Center point</param>
/// <returns>Regular Polygon with CCW points</returns>
static Polygon create_circle(double radius, size_t count_points = 10, const Point& center = Point(0,0)){
return create_regular(count_points, radius, center);
}
/// <summary>
/// Create triangle with same length for all sides
/// </summary>
/// <param name="edge_size">triangel edge size</param>
/// <returns>Equilateral triangle</returns>
static Polygon create_equilateral_triangle(double edge_size);
/// <summary>
/// Create triangle with two side with same size
/// </summary>
/// <param name="side">Size of unique side</param>
/// <param name="height">triangle height</param>
/// <returns>Isosceles Triangle </returns>
static Polygon create_isosceles_triangle(double side, double height);
/// <summary>
/// Create squar with center in [0,0]
/// </summary>
/// <param name="size"></param>
/// <returns>Square</returns>
static Polygon create_square(double size);
/// <summary>
/// Create rect with center in [0,0]
/// </summary>
/// <param name="width">width</param>
/// <param name="height">height</param>
/// <returns>Rectangle</returns>
static Polygon create_rect(double width, double height);
};
} // namespace Slic3r::sla
#endif // slic3r_SLA_SuppotstIslands_PolygonUtils_hpp_

View File

@ -33,6 +33,11 @@ struct SampleConfig
// Maximal width of line island supported by zig zag
double max_width_for_zig_zag_supportr_line = 1.;
// Term criteria for end of alignment
// Minimal change in manhatn move of support position before termination
coord_t minimal_move = 1;
// Maximal count of align iteration
size_t count_iteration = 100;
};
} // namespace Slic3r::sla
#endif // slic3r_SLA_SuppotstIslands_SampleConfig_hpp_

View File

@ -10,6 +10,8 @@
#include <magic_enum/magic_enum.hpp>
#include <libslic3r/VoronoiVisualUtils.hpp>
#include <libslic3r/ClipperUtils.hpp> // allign
using namespace Slic3r::sla;
SupportIslandPoint SampleIslandUtils::create_point(
@ -187,18 +189,48 @@ Slic3r::Points SampleIslandUtils::to_points(const SupportIslandPoints &support_p
return points;
}
void SampleIslandUtils::align_samples(SupportIslandPoints &samples, double max_distance)
void SampleIslandUtils::align_samples(SupportIslandPoints &samples,
const ExPolygon & island,
const SampleConfig & config)
{
size_t count_iteration = config.count_iteration; // copy
while (--count_iteration > 1) {
coord_t max_move = align_once(samples, island, config);
if (max_move < config.minimal_move) break;
}
}
coord_t SampleIslandUtils::align_once(SupportIslandPoints &samples,
const ExPolygon & island,
const SampleConfig & config)
{
using VD = Slic3r::Geometry::VoronoiDiagram;
VD vd;
Slic3r::Points points = SampleIslandUtils::to_points(samples);
construct_voronoi(points.begin(), points.end(), &vd);
for (const VD::cell_type &cell : vd.cells()) {
SupportIslandPoint& sample = samples[cell.source_index()];
Polygon polygon = VoronoiGraphUtils::to_polygon(cell, points, max_distance);
}
// create voronoi diagram with points
construct_voronoi(points.begin(), points.end(), &vd);
coord_t max_move = 0;
for (const VD::cell_type &cell : vd.cells()) {
SupportIslandPoint &sample = samples[cell.source_index()];
if (!sample.can_move()) continue;
Polygon polygon = VoronoiGraphUtils::to_polygon(cell, points, config.max_distance);
Polygons intersections = Slic3r::intersection(island, ExPolygon(polygon));
const Polygon *island_cell = nullptr;
for (const Polygon &intersection : intersections) {
if (intersection.contains(sample.point)) {
island_cell = &intersection;
break;
}
}
assert(island_cell != nullptr);
Point center = island_cell->centroid();
Point move = center - sample.point;
coord_t act_move = move.x() + move.y(); // manhatn distance
if (max_move < act_move) max_move = act_move;
}
return max_move;
}
SupportIslandPoints SampleIslandUtils::sample_center_line(
@ -215,8 +247,6 @@ SupportIslandPoints SampleIslandUtils::sample_center_line(
if (path.circles.empty()) return result;
sample_center_circles(path, cfg, result);
align_samples(result, cfg.max_sample_distance);
return result;
}

View File

@ -133,10 +133,28 @@ public:
/// <summary>
/// keep same distances between support points
/// call once align
/// </summary>
/// <param name="result">In/Out vector of support points</param>
/// <param name="max_distance">Maximal distance between neighbor points</param>
static void align_samples(SupportIslandPoints &samples, double max_distance);
/// <param name="samples">In/Out support points to be alligned</param>
/// <param name="island">Area for sampling, border for position of samples</param>
/// <param name="config"> Sampling configuration
/// Maximal distance between neighbor points +
/// Term criteria for align: Minimal sample move and Maximal count of iteration</param>
static void align_samples(SupportIslandPoints &samples,
const ExPolygon & island,
const SampleConfig &config);
/// <summary>
/// once align
/// </summary>
/// <param name="samples">In/Out support points to be alligned</param>
/// <param name="island">Area for sampling, border for position of samples</param>
/// <param name="config"> Sampling configuration
/// Maximal distance between neighbor points +
/// Term criteria for align: Minimal sample move and Maximal count of iteration</param>
static coord_t align_once(SupportIslandPoints &samples,
const ExPolygon & island,
const SampleConfig & config);
static void draw(SVG & svg,
const SupportIslandPoints &supportIslandPoints,

View File

@ -1,6 +1,7 @@
#ifndef slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_
#define slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_
#include <set>
#include <libslic3r/Point.hpp>
#include "VoronoiGraph.hpp"
@ -34,6 +35,29 @@ struct SupportIslandPoint
VoronoiGraph::Position position)
: point(std::move(point)), type(type), position(position)
{}
bool can_move() const{
// use shorter list
/*
static const std::set<Type> can_move({
Type::center_line,
Type::center_circle,
Type::center_circle_end,
Type::center_circle_end2
});
return can_move.find(type) != can_move.end();
/*/
static const std::set<Type> cant_move({
Type::one_center_point,
Type::two_points
});
return cant_move.find(type) == cant_move.end();
//*/
}
};
using SupportIslandPoints = std::vector<SupportIslandPoint>;

View File

@ -7,22 +7,53 @@
#include "ParabolaUtils.hpp"
#include "LineUtils.hpp"
#include "PointUtils.hpp"
#include "PolygonUtils.hpp"
#include <libslic3r/VoronoiVisualUtils.hpp>
#define SLA_CELL_2_POLYGON_DEBUG
using namespace Slic3r::sla;
Slic3r::Line VoronoiGraphUtils::to_line(const VD::edge_type &edge) {
assert(edge.is_linear());
assert(edge.is_finite());
return Line(Point(edge.vertex0()->x(), edge.vertex0()->y()),
Point(edge.vertex1()->x(), edge.vertex1()->y()));
coord_t VoronoiGraphUtils::to_coord(const VD::coordinate_type &coord)
{
static const VD::coordinate_type min_val =
static_cast<VD::coordinate_type>(std::numeric_limits<coord_t>::min());
static const VD::coordinate_type max_val =
static_cast<VD::coordinate_type>(std::numeric_limits<coord_t>::max());
if (coord > max_val) return std::numeric_limits<coord_t>::max();
if (coord < min_val) return std::numeric_limits<coord_t>::min();
return static_cast<coord_t>(std::round(coord));
}
Slic3r::Point VoronoiGraphUtils::to_point(const VD::vertex_type *vertex)
{
return Point(to_coord(vertex->x()), to_coord(vertex->y()));
}
bool VoronoiGraphUtils::is_coord_in_limits(const VD::coordinate_type &coord,
const coord_t & source,
double max_distance)
{
VD::coordinate_type min_val = source - max_distance;
VD::coordinate_type max_val = source + max_distance;
if (coord > max_val) return false;
if (coord < min_val) return false;
return true;
}
bool VoronoiGraphUtils::is_point_in_limits(const VD::vertex_type *vertex,
const Point & source,
double max_distance)
{
if (vertex == nullptr) return false;
return is_coord_in_limits(vertex->x(), source.x(), max_distance) &&
is_coord_in_limits(vertex->y(), source.y(), max_distance);
}
// create line segment between (in the middle) points. With size depend on their distance
Slic3r::Line VoronoiGraphUtils::to_line(Point point1,
Point point2,
double maximal_distance)
Slic3r::Line VoronoiGraphUtils::create_line_between_source_points(
const Point &point1, const Point &point2, double maximal_distance)
{
Point middle = (point1 + point2) / 2;
Point diff = point1 - point2;
@ -44,57 +75,84 @@ bool is_oposit_direction(const Slic3r::Point &p1, const Slic3r::Point &p2) {
return (p1.y() > 0) != (p2.y() > 0);
}
Slic3r::Line VoronoiGraphUtils::to_line(const VD::edge_type & edge,
const Points &points,
double maximal_distance)
std::optional<Slic3r::Line> VoronoiGraphUtils::to_line(
const VD::edge_type &edge, const Points &points, double maximal_distance)
{
assert(edge.is_linear());
assert(edge.is_primary());
const Point &p1 = retrieve_point(points, *edge.cell());
const Point &p2 = retrieve_point(points, *edge.twin()->cell());
const VD::vertex_type *v0 = edge.vertex0();
const VD::vertex_type *v1 = edge.vertex1();
if (edge.is_finite()) return to_line(edge);
bool use_v1 = false; // v0 == nullptr or out of limits
bool use_double_precision = false;
bool use_both = false;
if (edge.is_finite()) {
bool is_v0_in_limit = is_point_in_limits(v0, p1, maximal_distance);
bool is_v1_in_limit = is_point_in_limits(v1, p1, maximal_distance);
if (!is_v0_in_limit) {
use_v1 = true;
if (!is_v1_in_limit) {
use_double_precision = true;
use_both = true;
}
} else if (is_v1_in_limit) {
// normal full edge line segment
return Line(to_point(v0), to_point(v1));
}
} else if (v0 == nullptr) {
if (v1 == nullptr)
{// both vertex are nullptr, create edge between points
return create_line_between_source_points(p1, p2, maximal_distance);
}
if (!is_point_in_limits(v1, p1, maximal_distance))
use_double_precision = true;
use_v1 = true;
} else if (!is_point_in_limits(v0, p1, maximal_distance)) {
use_double_precision = true;
if (v1 != nullptr)
use_v1 = true; // v1 is in
}
const VD::cell_type &cell1 = *edge.cell();
const VD::cell_type &cell2 = *edge.twin()->cell();
assert(cell1.contains_point());
assert(cell2.contains_point());
Point p1 = retrieve_point(points, cell1);
Point p2 = retrieve_point(points, cell2);
if (edge.vertex0() == nullptr && edge.vertex1() == nullptr)
return to_line(p1, p2, maximal_distance);
bool is_v0_null = edge.vertex0() == nullptr;
if (is_v0_null) std::swap(p1, p2);
Point direction(p1.y() - p2.y(), p2.x() - p1.x());
auto get_koef = [&](const Point &v)->double {
/*/
// faster but less preciss version
Point direction = (use_v1) ?
Point(p2.y() - p1.y(), p1.x() - p2.x()) :
Point(p1.y() - p2.y(), p2.x() - p1.x());
const VD::vertex_type* edge_vertex = (use_v1) ? v1 : v0;
// koeficient for crop line
double koef = 1.;
if (!use_double_precision) {
// only half edges
Point vertex = to_point(edge_vertex);
/*// faster but less preciss version
double abs_max_dir = (std::max)(fabs(direction.x()),
fabs(direction.y()));
return 2 * maximal_distance / abs_max_dir;
fabs(direction.y())); return 2 * maximal_distance / abs_max_dir;
//*/
// slower but more precisse version
double dir_size = direction.cast<double>().operatorNorm();
Point middle = (p1 + p2) / 2;
Point to_middle = middle - v;
Point to_middle = middle - vertex;
double to_middle_size = to_middle.cast<double>().operatorNorm();
double from_middle_size = sqrt(maximal_distance * maximal_distance -
to_middle_size * to_middle_size);
if (is_oposit_direction(direction, to_middle)) to_middle_size *= -1;
return (from_middle_size + to_middle_size)/dir_size;
};
if (is_v0_null) {
Point v1(edge.vertex1()->x(), edge.vertex1()->y());
Point a = v1 + direction * get_koef(v1);
return Line(a, v1);
} else {
Point v0(edge.vertex0()->x(), edge.vertex0()->y());
Point b = v0 + direction * get_koef(v0);
return Line(v0, b);
bool is_opposit = is_oposit_direction(direction, to_middle);
if (is_opposit) to_middle_size *= -1;
koef = (from_middle_size + to_middle_size) / dir_size;
Point line_point = vertex + direction * koef;
return Line(vertex, line_point);
}
std::optional<Linef> segment;
if (use_both) {
Linef edge_segment(Vec2d(v0->x(), v0->y()), Vec2d(v1->x(), v1->y()));
segment = LineUtils::crop_line(edge_segment, p1, maximal_distance);
} else {
Vec2d ray_point(edge_vertex->x(), edge_vertex->y());
Linef ray = Linef(ray_point, ray_point + direction.cast<double>());
segment = LineUtils::crop_half_ray(ray, p1, maximal_distance);
}
if (!segment.has_value()) return {};
return Line(segment->a.cast<coord_t>(), segment->b.cast<coord_t>());
}
Slic3r::Polygon VoronoiGraphUtils::to_polygon(const Lines &lines,
@ -103,12 +161,14 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const Lines &lines,
double minimal_distance,
size_t count_points)
{
assert(lines.size() >= 1);
assert(minimal_distance > 0.);
assert(maximal_distance > minimal_distance);
assert(count_points >= 3);
if (lines.empty())
return PolygonUtils::create_regular(count_points, maximal_distance, center);
Points points;
points.reserve(lines.size());
points.reserve(std::max(lines.size(), count_points));
const Line *prev_line = &lines.back();
double max_angle = 2 * M_PI / count_points;
for (const Line &line : lines) {
@ -136,13 +196,16 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const Lines &lines,
points.push_back(p1);
for (size_t i = 1; i < count_segment; i++) {
double angle = a1 + i*increase_angle;
Point direction(
static_cast<coord_t>(cos(angle) * maximal_distance),
static_cast<coord_t>(sin(angle) * maximal_distance));
points.push_back(center + direction);
double x = cos(angle) * maximal_distance + center.x();
assert(x < std::numeric_limits<coord_t>::max());
assert(x > std::numeric_limits<coord_t>::min());
double y = sin(angle) * maximal_distance + center.y();
assert(y < std::numeric_limits<coord_t>::max());
assert(y > std::numeric_limits<coord_t>::min());
points.emplace_back(x,y);
}
points.push_back(p2);
}
return Polygon(points);
}
@ -152,16 +215,18 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell,
double maximal_distance)
{
const VD::edge_type *edge = cell.incident_edge();
Lines lines;
Point center = points[cell.source_index()];
// Convenient way to iterate edges around Voronoi cell.
do {
assert(edge->is_linear());
if (edge->is_primary()) {
Line line = to_line(*edge, points, maximal_distance);
if (!PointUtils::is_ccw(line.a, line.b, center)) std::swap(line.a, line.b);
lines.push_back(line);
std::optional<Line> line = to_line(*edge, points, maximal_distance);
if (line.has_value()) {
if (!PointUtils::is_ccw(line->a, line->b, center))
std::swap(line->a, line->b);
lines.push_back(line.value());
}
}
edge = edge->next();
} while (edge != cell.incident_edge());
@ -170,7 +235,7 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell,
double min_distance = maximal_distance / 1000.;
size_t count_point = 6; // count added points
Slic3r::Polygon polygon = to_polygon(lines, center, maximal_distance, min_distance, count_point);
#ifdef SLA_SUPPORTPOINTGEN_DEBUG
#ifdef SLA_CELL_2_POLYGON_DEBUG
{
std::cout << "cell " << cell.source_index() << " has " << lines.size() << "edges" << std::endl;
BoundingBox bbox(center - Point(maximal_distance, maximal_distance),
@ -188,7 +253,7 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell,
}
svg.draw(center, "red", maximal_distance / 100);
}
#endif /* SLA_SUPPORTPOINTGEN_DEBUG */
#endif /* SLA_CELL_2_POLYGON_DEBUG */
return polygon;
}
@ -208,7 +273,7 @@ VoronoiGraph::Node *VoronoiGraphUtils::getNode(VoronoiGraph & graph,
// const VD::cell_type * cell2 = edge.twin()->cell();
const Line &line = lines[cell->source_index()];
// const Line & line1 = lines[cell2->source_index()];
Point point(vertex->x(), vertex->y());
Point point = to_point(vertex);
double distance = line.distance_to(point);
auto &[iterator,
@ -229,9 +294,10 @@ Slic3r::Point VoronoiGraphUtils::retrieve_point(const Lines & lines,
lines[cell.source_index()].b;
}
Slic3r::Point VoronoiGraphUtils::retrieve_point(const Points & points,
const VD::cell_type &cell)
const Slic3r::Point &VoronoiGraphUtils::retrieve_point(
const Points &points, const VD::cell_type &cell)
{
assert(cell.contains_point());
assert(cell.source_category() == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT);
return points[cell.source_index()];
}
@ -273,8 +339,8 @@ double VoronoiGraphUtils::calculate_length_of_parabola(
const VD::edge_type & edge,
const Lines & lines)
{
Point v0{edge.vertex0()->x(), edge.vertex0()->y()};
Point v1{edge.vertex1()->x(), edge.vertex1()->y()};
Point v0 = to_point(edge.vertex0());
Point v1 = to_point(edge.vertex1());
ParabolaSegment parabola(get_parabola(edge, lines), v0, v1);
return ParabolaUtils::length(parabola);
}
@ -296,8 +362,8 @@ double VoronoiGraphUtils::calculate_length(
double VoronoiGraphUtils::calculate_max_width(
const VD::edge_type &edge, const Lines &lines)
{
Point v0{edge.vertex0()->x(), edge.vertex0()->y()};
Point v1{edge.vertex1()->x(), edge.vertex1()->y()};
Point v0 = to_point(edge.vertex0());
Point v1 = to_point(edge.vertex1());
if (edge.is_linear()) {
// edge line could be initialized by 2 points
@ -822,10 +888,8 @@ void VoronoiGraphUtils::draw(SVG & svg,
svg.draw(Point(key->x(), key->y()), "lightgray", width);
for (const auto &n : value.neighbors) {
if (n.edge->vertex0() > n.edge->vertex1()) continue;
auto v0 = *n.edge->vertex0();
Point from(v0.x(), v0.y());
auto v1 = *n.edge->vertex1();
Point to(v1.x(), v1.y());
Point from = to_point(n.edge->vertex0());
Point to = to_point(n.edge->vertex1());
svg.draw(Line(from, to), "gray", width);
Point center = from + to;
@ -850,8 +914,9 @@ void VoronoiGraphUtils::draw(SVG & svg,
prev_node = node;
continue;
}
Point from(prev_node->vertex->x(), prev_node->vertex->y());
Point to(node->vertex->x(), node->vertex->y());
Point from = to_point(prev_node->vertex);
Point to = to_point(node->vertex);
svg.draw(Line(from, to), color, width);
svg.draw_text(from, std::to_string(index - 1).c_str(), color);
@ -872,8 +937,7 @@ void VoronoiGraphUtils::draw(SVG & svg,
draw(svg, circle.nodes, width, circlePathColor, true);
Point center(0, 0);
for (auto p : circle.nodes) {
center.x() += p->vertex->x();
center.y() += p->vertex->y();
center += to_point(p->vertex);
}
center.x() /= circle.nodes.size();
center.y() /= circle.nodes.size();

View File

@ -27,24 +27,51 @@ public:
VoronoiGraphUtils() = delete;
/// <summary>
/// Convert line edge segment to slicer line
/// only for line edge
/// only for finite line
/// Convert coordinate type between voronoi and slicer format
/// </summary>
/// <param name="edge">line edge</param>
/// <returns>line</returns>
static Slic3r::Line to_line(const VD::edge_type &edge);
/// <param name="coor">Coordinate</param>
/// <returns>When it is possible than cast it otherwise empty optional</returns>
static coord_t to_coord(const VD::coordinate_type &coord);
/// <summary>
/// Convert Point type to slicer point
/// </summary>
/// <param name="vertex">Input point pointer</param>
/// <returns>When it is possible to convert than convert otherwise empty optional</returns>
static Slic3r::Point to_point(const VD::vertex_type *vertex);
/// <summary>
/// check if coord is in limits for coord_t
/// </summary>
/// <param name="coord">input value</param>
/// <param name="source">VD source point coordinate</param>
/// <param name="max_distance">Maximal distance from source</param>
/// <returns>True when coord is in +- max_distance from source otherwise FALSE.</returns>
static bool is_coord_in_limits(const VD::coordinate_type &coord,
const coord_t & source,
double max_distance);
/// <summary>
/// Check x and y values of vertex
/// </summary>
/// <param name="vertex">input vertex</param>
/// <param name="source">VD source point</param>
/// <param name="max_distance">Maximal distance from source</param>
/// <returns>True when both coord are in limits given by source and max distance otherwise FALSE</returns>
static bool is_point_in_limits(const VD::vertex_type *vertex,
const Point & source,
double max_distance);
/// <summary>
/// Private function to help convert edge without vertex to line
/// </summary>
/// <param name="point1">VD Source point</param>
/// <param name="point2">VD Source point</param>
/// <param name="point1">VD source point</param>
/// <param name="point2">VD source point</param>
/// <param name="maximal_distance">Maximal distance from source point</param>
/// <returns>Line segment between lines</returns>
static Slic3r::Line to_line(Point point1,
Point point2,
double maximal_distance);
static Slic3r::Line create_line_between_source_points(
const Point &point1, const Point &point2, double maximal_distance);
/// <summary>
/// Convert edge to line
/// only for line edge
@ -54,9 +81,9 @@ public:
/// <param name="edge"></param>
/// <param name="points">Source point for voronoi diagram</param>
/// <param name="maximal_distance">Maximal distance from source point</param>
/// <returns>Croped line</returns>
static Slic3r::Line to_line(const VD::edge_type & edge,
const Points &points,
/// <returns>Croped line, when all line segment is out of max distance return empty optional</returns>
static std::optional<Slic3r::Line> to_line(const VD::edge_type &edge,
const Points & points,
double maximal_distance);
/// <summary>
/// close polygon defined by lines
@ -112,7 +139,7 @@ public:
/// <param name="points">Source of Voronoi diagram</param>
/// <param name="cell">Cell inside of Voronoi diagram</param>
/// <returns>Point from source points.</returns>
static Point retrieve_point(const Points &points, const VD::cell_type &cell);
static const Point& retrieve_point(const Points &points, const VD::cell_type &cell);
static Slic3r::Point get_parabola_point(const VD::edge_type &parabola, const Slic3r::Lines &lines);
static Slic3r::Line get_parabola_line(const VD::edge_type &parabola, const Lines &lines);

View File

@ -9,6 +9,7 @@
#include <libslic3r/SLA/SupportIslands/SampleConfig.hpp>
#include <libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp>
#include <libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp>
#include <libslic3r/SLA/SupportIslands/PolygonUtils.hpp>
#include "sla_test_utils.hpp"
@ -138,59 +139,18 @@ TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]")
REQUIRE(!pts.empty());
}
// all triangle side are same length
Slic3r::Polygon equilateral_triangle(double size)
{
return {{.0, .0},
{size, .0},
{size / 2., sqrt(size * size - size * size / 4)}};
}
// two side of triangle are same size
Slic3r::Polygon isosceles_triangle(double side, double height)
{
return {{-side / 2, 0.}, {side / 2, 0.}, {.0, height}};
}
Slic3r::Polygon square(double size)
{
double size_2 = size / 2;
return {{-size_2, size_2},
{-size_2, -size_2},
{size_2, -size_2},
{size_2, size_2}};
}
Slic3r::Polygon rect(double x, double y){
double x_2 = x / 2;
double y_2 = y / 2;
return {{-x_2, y_2}, {-x_2, -y_2}, {x_2, -y_2}, {x_2, y_2}};
}
Slic3r::Polygon circle(double radius, size_t count_line_segments) {
// CCW: couter clock wise, CW: clock wise
Points circle;
circle.reserve(count_line_segments);
for (size_t i = 0; i < count_line_segments; ++i) {
double alpha = (2 * M_PI * i) / count_line_segments;
double sina = sin(alpha);
double cosa = cos(alpha);
circle.emplace_back(-radius * sina, radius * cosa);
}
return Slic3r::Polygon(circle);
}
Slic3r::Polygon create_cross_roads(double size, double width)
{
auto r1 = rect( 5.3 * size, width);
auto r1 = PolygonUtils::create_rect(5.3 * size, width);
r1.rotate(3.14/4);
r1.translate(2 * size, width / 2);
auto r2 = rect(6.1*size, 3/4.*width);
auto r2 = PolygonUtils::create_rect(6.1 * size, 3 / 4. * width);
r2.rotate(-3.14 / 5);
r2.translate(3 * size, width / 2);
auto r3 = rect(7.9*size, 4/5.*width);
auto r3 = PolygonUtils::create_rect(7.9 * size, 4 / 5. * width);
r3.translate(2*size, width/2);
auto r4 = rect(5 / 6. * width, 5.7 * size);
auto r4 = PolygonUtils::create_rect(5 / 6. * width, 5.7 * size);
r4.translate(-size,3*size);
Polygons rr = union_(Polygons({r1, r2, r3, r4}));
return rr.front();
@ -198,7 +158,8 @@ Slic3r::Polygon create_cross_roads(double size, double width)
ExPolygon create_trinagle_with_hole(double size)
{
return ExPolygon(equilateral_triangle(size), {{size / 4, size / 4},
return ExPolygon(PolygonUtils::create_equilateral_triangle(size),
{{size / 4, size / 4},
{size / 2, size / 2},
{size / 2, size / 4}});
}
@ -206,14 +167,14 @@ ExPolygon create_trinagle_with_hole(double size)
ExPolygon create_square_with_hole(double size, double hole_size)
{
assert(sqrt(hole_size *hole_size / 2) < size);
auto hole = square(hole_size);
auto hole = PolygonUtils::create_square(hole_size);
hole.rotate(M_PI / 4.); // 45
hole.reverse();
return ExPolygon(square(size), hole);
return ExPolygon(PolygonUtils::create_square(size), hole);
}
ExPolygon create_square_with_4holes(double size, double hole_size) {
auto hole = square(hole_size);
auto hole = PolygonUtils::create_square(hole_size);
hole.reverse();
double size_4 = size / 4;
auto h1 = hole;
@ -224,7 +185,7 @@ ExPolygon create_square_with_4holes(double size, double hole_size) {
h3.translate(size_4, -size_4);
auto h4 = hole;
h4.translate(-size_4, -size_4);
ExPolygon result(square(size));
ExPolygon result(PolygonUtils::create_square(size));
result.holes = Polygons({h1, h2, h3, h4});
return result;
}
@ -233,14 +194,17 @@ ExPolygon create_square_with_4holes(double size, double hole_size) {
ExPolygon create_disc(double radius, double width, size_t count_line_segments)
{
double width_2 = width / 2;
auto hole = circle(radius-width_2, count_line_segments);
auto hole = PolygonUtils::create_circle(radius - width_2,
count_line_segments);
hole.reverse();
return ExPolygon(circle(radius + width_2, count_line_segments), hole);
return ExPolygon(PolygonUtils::create_circle(radius + width_2,
count_line_segments),
hole);
}
Slic3r::Polygon create_V_shape(double height, double line_width, double angle = M_PI/4) {
double angle_2 = angle / 2;
auto left_side = rect(line_width, height);
auto left_side = PolygonUtils::create_rect(line_width, height);
auto right_side = left_side;
right_side.rotate(-angle_2);
double small_move = cos(angle_2) * line_width / 2;
@ -248,7 +212,7 @@ Slic3r::Polygon create_V_shape(double height, double line_width, double angle =
right_side.translate(side_move,0);
left_side.rotate(angle_2);
left_side.translate(-side_move, 0);
auto bottom = rect(4 * small_move, line_width);
auto bottom = PolygonUtils::create_rect(4 * small_move, line_width);
bottom.translate(0., -cos(angle_2) * height / 2 + line_width/2);
Polygons polygons = union_(Polygons({left_side, right_side, bottom}));
return polygons.front();
@ -267,22 +231,22 @@ ExPolygons createTestIslands(double size)
{size / 7, size}});
ExPolygons result = {
// one support point
ExPolygon(equilateral_triangle(size)),
ExPolygon(square(size)),
ExPolygon(rect(size / 2, size)),
ExPolygon(isosceles_triangle(size / 2, 3 * size / 2)), // small sharp triangle
ExPolygon(circle(size/2, 10)),
ExPolygon(PolygonUtils::create_equilateral_triangle(size)),
ExPolygon(PolygonUtils::create_square(size)),
ExPolygon(PolygonUtils::create_rect(size / 2, size)),
ExPolygon(PolygonUtils::create_isosceles_triangle(size / 2, 3 * size / 2)), // small sharp triangle
ExPolygon(PolygonUtils::create_circle(size / 2, 10)),
create_square_with_4holes(size, size / 4),
create_disc(size/4, size / 4, 10),
ExPolygon(create_V_shape(2*size/3, size / 4)),
// two support points
ExPolygon(isosceles_triangle(size / 2, 3 * size)), // small sharp triangle
ExPolygon(rect(size / 2, 3 * size)),
ExPolygon(PolygonUtils::create_isosceles_triangle(size / 2, 3 * size)), // small sharp triangle
ExPolygon(PolygonUtils::create_rect(size / 2, 3 * size)),
ExPolygon(create_V_shape(1.5*size, size/3)),
// tiny line support points
ExPolygon(rect(size / 2, 10 * size)), // long line
ExPolygon(PolygonUtils::create_rect(size / 2, 10 * size)), // long line
ExPolygon(create_V_shape(size*4, size / 3)),
ExPolygon(create_cross_roads(size, size / 3)),
create_disc(3*size, size / 4, 30),
@ -290,8 +254,8 @@ ExPolygons createTestIslands(double size)
// still problem
// three support points
ExPolygon(equilateral_triangle(3 * size)),
ExPolygon(circle(size, 20)),
ExPolygon(PolygonUtils::create_equilateral_triangle(3 * size)),
ExPolygon(PolygonUtils::create_circle(size, 20)),
mountains,
create_trinagle_with_hole(size),
@ -444,23 +408,6 @@ TEST_CASE("Sampling speed test on FrogLegs", "[VoronoiSkeleton]")
}
}
/*
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Delaunay_triangulation_2.h>
void cgal_test(const SupportIslandPoints &points, const ExPolygon &island) {
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using Delaunay = CGAL::Delaunay_triangulation_2<Kernel>;
std::vector<Kernel::Point_2> k_points;
k_points.reserve(points.size());
std::transform(points.begin(), points.end(), std::back_inserter(k_points),
[](const SupportIslandPoint &p) {
return Kernel::Point_2(p.point.x(), p.point.y());
});
Delaunay dt;
dt.insert(k_points.begin(), k_points.end());
std::cout << dt.number_of_vertices() << std::endl;
}*/
TEST_CASE("Small islands should be supported in center", "[SupGen][VoronoiSkeleton]")
{
double size = 3e7;

View File

@ -1,18 +1,47 @@
#include "sla_test_utils.hpp"
#include <libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp>
#include <libslic3r/VoronoiVisualUtils.hpp>
using namespace Slic3r;
using namespace Slic3r::sla;
TEST_CASE("Convert coordinate datatype", "[Voronoi]")
{
using VD = Slic3r::Geometry::VoronoiDiagram;
VD::coordinate_type coord = 101197493902.64694;
coord_t coord2 = VoronoiGraphUtils::to_coord(coord);
CHECK(coord2 > 100);
coord = -101197493902.64694;
coord2 = VoronoiGraphUtils::to_coord(coord);
CHECK(coord2 < -100);
coord = 12345.1;
coord2 = VoronoiGraphUtils::to_coord(coord);
CHECK(coord2 == 12345);
coord = -12345.1;
coord2 = VoronoiGraphUtils::to_coord(coord);
CHECK(coord2 == -12345);
coord = 12345.9;
coord2 = VoronoiGraphUtils::to_coord(coord);
CHECK(coord2 == 12346);
coord = -12345.9;
coord2 = VoronoiGraphUtils::to_coord(coord);
CHECK(coord2 == -12346);
}
void check(Slic3r::Points points, double max_distance) {
using VD = Slic3r::Geometry::VoronoiDiagram;
VD vd;
construct_voronoi(points.begin(), points.end(), &vd);
double max_area = 3.15 * 4 * max_distance*max_distance; // circle
double max_area = M_PI * max_distance*max_distance; // circle = Pi * r^2
for (const VD::cell_type &cell : vd.cells()) {
Slic3r::Polygon polygon = VoronoiGraphUtils::to_polygon(cell, points, max_distance);
CHECK(polygon.area() < max_area);
CHECK(polygon.contains(points[cell.source_index()]));
}
}
@ -41,6 +70,21 @@ TEST_CASE("Polygon from cell", "[Voronoi]")
Slic3r::Points middle_point2({Point(half_size, half_size), Point(-size, -size), Point(-size, size),
Point(size, -size), Point(size, size)});
check(middle_point2, max_distance);
Slic3r::Points diagonal_points({{-123473762, 71287970},
{-61731535, 35684428},
{0, 0},
{61731535, -35684428},
{123473762, -71287970}});
double diagonal_max_distance = 5e7;
check(diagonal_points, diagonal_max_distance);
int scale = 10;
Slic3r::Points diagonal_points2;
std::transform(diagonal_points.begin(), diagonal_points.end(),
std::back_inserter(diagonal_points2),
[&](const Slic3r::Point &p) { return p/scale; });
check(diagonal_points2, diagonal_max_distance / scale);
}