mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
Add simple polygon class (#2750)
* Add simple polygon class * Add documentation for fill_convex_polygon * Rename alpha_blend to antialias * Update dlib/geometry/polygon_abstract.h Co-authored-by: Davis E. King <davis685@gmail.com> * Update dlib/geometry/polygon_abstract.h Co-authored-by: Davis E. King <davis685@gmail.com> * Update dlib/geometry/polygon_abstract.h Co-authored-by: Davis E. King <davis685@gmail.com> * Update dlib/geometry/polygon_abstract.h Co-authored-by: Davis E. King <davis685@gmail.com> * Update dlib/geometry/polygon_abstract.h Co-authored-by: Davis E. King <davis685@gmail.com> * Update dlib/geometry/polygon.h Co-authored-by: Davis E. King <davis685@gmail.com> * Update documentation for get_convex_shape * Add tests for the polygon class * Remove new line --------- Co-authored-by: Davis E. King <davis685@gmail.com>
This commit is contained in:
parent
9a13229970
commit
4ffa9b02a5
@ -9,6 +9,7 @@
|
||||
#include "geometry/border_enumerator.h"
|
||||
#include "geometry/point_transforms.h"
|
||||
#include "geometry/line.h"
|
||||
#include "geometry/polygon.h"
|
||||
|
||||
#endif // DLIB_GEOMETRy_HEADER
|
||||
|
||||
|
92
dlib/geometry/polygon.h
Normal file
92
dlib/geometry/polygon.h
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (C) 2022 Davis E. King (davis@dlib.net), Adrià Arrufat
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#ifndef DLIB_POLYGOn_
|
||||
#define DLIB_POLYGOn_
|
||||
|
||||
#include "polygon_abstract.h"
|
||||
#include "rectangle.h"
|
||||
#include "vector.h"
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
class polygon
|
||||
{
|
||||
public:
|
||||
using size_type = std::vector<point>::size_type;
|
||||
|
||||
polygon(std::vector<point> points) : points(std::move(points)) {}
|
||||
|
||||
size_type size() const { return points.size(); }
|
||||
|
||||
point& operator[](const size_type idx) { return points[idx]; }
|
||||
const point& operator[](const size_type idx) const { return points[idx]; }
|
||||
const point& at(const size_type idx) const { return points.at(idx); }
|
||||
|
||||
rectangle get_rect() const
|
||||
{
|
||||
rectangle rect;
|
||||
for (const auto& p : points)
|
||||
rect += p;
|
||||
return rect;
|
||||
}
|
||||
|
||||
double area() const { return polygon_area(points); }
|
||||
|
||||
template <typename alloc>
|
||||
void get_convex_shape (
|
||||
const long top,
|
||||
const long bottom,
|
||||
std::vector<double, alloc>& left_boundary,
|
||||
std::vector<double, alloc>& right_boundary
|
||||
) const
|
||||
{
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
left_boundary.assign(bottom-top+1, std::numeric_limits<double>::infinity());
|
||||
right_boundary.assign(bottom-top+1, -std::numeric_limits<double>::infinity());
|
||||
|
||||
// trace out the points along the edge of the polynomial and record them
|
||||
for (unsigned long i = 0; i < points.size(); ++i)
|
||||
{
|
||||
const point p1 = points[i];
|
||||
const point p2 = points[(i+1)%points.size()];
|
||||
|
||||
if (p1.y() == p2.y())
|
||||
{
|
||||
if (top <= p1.y() && p1.y() <= bottom)
|
||||
{
|
||||
const long y = p1.y() - top;
|
||||
const double xmin = min(p1.x(), p2.x());
|
||||
const double xmax = min(p1.x(), p2.x());
|
||||
left_boundary[y] = min(left_boundary[y], xmin);
|
||||
right_boundary[y] = max(right_boundary[y], xmax);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here we trace out the line from p1 to p2 and record where it hits.
|
||||
|
||||
// x = m*y + b
|
||||
const double m = (p2.x() - p1.x())/(double)(p2.y()-p1.y());
|
||||
const double b = p1.x() - m*p1.y(); // because: x1 = m*y1 + b
|
||||
|
||||
const long ymin = max(top,min(p1.y(), p2.y()));
|
||||
const long ymax = min(bottom,max(p1.y(), p2.y()));
|
||||
for (long y = ymin; y <= ymax; ++y)
|
||||
{
|
||||
const double x = m*y + b;
|
||||
const unsigned long idx = y-top;
|
||||
left_boundary[idx] = min(left_boundary[idx], x);
|
||||
right_boundary[idx] = max(right_boundary[idx], x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<point> points;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // polygon_h_INCLUDED
|
108
dlib/geometry/polygon_abstract.h
Normal file
108
dlib/geometry/polygon_abstract.h
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright (C) 2022 Davis E. King (davis@dlib.net), Adrià Arrufat
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#undef DLIB_POLYGOn_ABSTRACT_H_
|
||||
#ifdef DLIB_POLYGOn_ABSTRACT_H_
|
||||
|
||||
#include "rectangle.h"
|
||||
#include "vector.h"
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
class polygon
|
||||
{
|
||||
/*!
|
||||
WHAT THIS OBJECT REPRESENTS
|
||||
This object represents a polygon inside a Cartesian coordinate system.
|
||||
It is just a wrapper class for a std::vector<point> with some added
|
||||
methods.
|
||||
|
||||
Note that the origin of the coordinate system, i.e. (0,0), is located
|
||||
at the upper left corner. That is, points such as (1,1) or (3,5)
|
||||
represent locations that are below and to the right of the origin.
|
||||
!*/
|
||||
|
||||
public:
|
||||
using size_type = std::vector<point>::size_type;
|
||||
|
||||
polygon(std::vector<point> points);
|
||||
/*!
|
||||
ensures
|
||||
- #*this represents a polygon defined by points.
|
||||
!*/
|
||||
|
||||
size_type size() const;
|
||||
/*!
|
||||
ensures
|
||||
- returns the number of points in the polygon.
|
||||
!*/
|
||||
|
||||
point& operator[](const size_type idx);
|
||||
/*!
|
||||
requires
|
||||
- idx < size()
|
||||
ensures
|
||||
- returns the point of the polygon at index idx.
|
||||
!*/
|
||||
|
||||
const point& operator[](const size_type idx) const;
|
||||
/*!
|
||||
requires
|
||||
- idx < size()
|
||||
ensures
|
||||
- returns the point of the polygon at index idx.
|
||||
!*/
|
||||
|
||||
const point& at(const size_type idx) const;
|
||||
/*!
|
||||
ensures
|
||||
- returns the point of the polygon at index idx.
|
||||
throws
|
||||
- std::out_of_range if idx >= size()
|
||||
!*/
|
||||
|
||||
rectangle get_rect() const;
|
||||
/*!
|
||||
ensures
|
||||
- returns smallest rectangle that contains all points in the polygon.
|
||||
!*/
|
||||
|
||||
double area() const;
|
||||
/*!
|
||||
ensures
|
||||
- If you walk the points of #*this in order to make a closed polygon, what is its
|
||||
area? This function returns that area. It uses the shoelace formula to
|
||||
compute the result and so works for general non-self-intersecting polygons.
|
||||
!*/
|
||||
|
||||
template <typename alloc>
|
||||
void get_convex_shape (
|
||||
const long top,
|
||||
const long bottom,
|
||||
std::vector<double, alloc>& left_boundary,
|
||||
std::vector<double, alloc>& right_boundary
|
||||
) const;
|
||||
/*!
|
||||
requires
|
||||
- 0 <= top <= bottom
|
||||
ensures
|
||||
- interprets points as the coordinates defining a convex polygon. In
|
||||
particular, we interpret points as a list of the vertices of the polygon
|
||||
and assume they are ordered in clockwise order.
|
||||
- #left_boundary.size() == bottom-top+1
|
||||
- #right_boundary.size() == bottom-top+1
|
||||
- for all top <= y <= bottom:
|
||||
- #left_boundary[y-top] == the x coordinate for the left most side of
|
||||
the polygon at coordinate y.
|
||||
- #right_boundary[y-top] == the x coordinate for the right most side of
|
||||
the polygon at coordinate y.
|
||||
|
||||
Note that it isn't illegal to call this method if the polygon defined by
|
||||
points isn't convex. In that case, we won't return a convex shape.
|
||||
!*/
|
||||
|
||||
private:
|
||||
std::vector<point> points;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // DLIB_POLYGOn_ABSTRACT_H_
|
@ -440,85 +440,10 @@ namespace dlib
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
namespace impl
|
||||
{
|
||||
|
||||
template <typename alloc>
|
||||
void get_convex_polygon_shape (
|
||||
const std::vector<point>& points,
|
||||
const long top,
|
||||
const long bottom,
|
||||
std::vector<double,alloc>& left_boundary,
|
||||
std::vector<double,alloc>& right_boundary
|
||||
)
|
||||
/*!
|
||||
requires
|
||||
- 0 <= top <= bottom
|
||||
ensures
|
||||
- interprets points as the coordinates defining a convex polygon. In
|
||||
particular, we interpret points as a list of the vertices of the polygon
|
||||
and assume they are ordered in clockwise order.
|
||||
- #left_boundary.size() == bottom-top+1
|
||||
- #right_boundary.size() == bottom-top+1
|
||||
- for all top <= y <= bottom:
|
||||
- #left_boundary[y-top] == the x coordinate for the left most side of
|
||||
the polygon at coordinate y.
|
||||
- #right_boundary[y-top] == the x coordinate for the right most side of
|
||||
the polygon at coordinate y.
|
||||
!*/
|
||||
{
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
left_boundary.assign(bottom-top+1, std::numeric_limits<double>::infinity());
|
||||
right_boundary.assign(bottom-top+1, -std::numeric_limits<double>::infinity());
|
||||
|
||||
// trace out the points along the edge of the polynomial and record them
|
||||
for (unsigned long i = 0; i < points.size(); ++i)
|
||||
{
|
||||
const point p1 = points[i];
|
||||
const point p2 = points[(i+1)%points.size()];
|
||||
|
||||
if (p1.y() == p2.y())
|
||||
{
|
||||
if (top <= p1.y() && p1.y() <= bottom)
|
||||
{
|
||||
const long y = p1.y() - top;
|
||||
const double xmin = min(p1.x(), p2.x());
|
||||
const double xmax = min(p1.x(), p2.x());
|
||||
left_boundary[y] = min(left_boundary[y], xmin);
|
||||
right_boundary[y] = max(right_boundary[y], xmax);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here we trace out the line from p1 to p2 and record where it hits.
|
||||
|
||||
// x = m*y + b
|
||||
const double m = (p2.x() - p1.x())/(double)(p2.y()-p1.y());
|
||||
const double b = p1.x() - m*p1.y(); // because: x1 = m*y1 + b
|
||||
|
||||
const long ymin = max(top,min(p1.y(), p2.y()));
|
||||
const long ymax = min(bottom,max(p1.y(), p2.y()));
|
||||
for (long y = ymin; y <= ymax; ++y)
|
||||
{
|
||||
const double x = m*y + b;
|
||||
const unsigned long idx = y-top;
|
||||
left_boundary[idx] = min(left_boundary[idx], x);
|
||||
right_boundary[idx] = max(right_boundary[idx], x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
template <typename pixel_type>
|
||||
void draw_solid_convex_polygon (
|
||||
const canvas& c,
|
||||
const std::vector<point>& polygon,
|
||||
const polygon& poly,
|
||||
const pixel_type& pixel,
|
||||
const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(),
|
||||
std::numeric_limits<long>::max(), std::numeric_limits<long>::max())
|
||||
@ -528,9 +453,7 @@ namespace dlib
|
||||
using std::min;
|
||||
const rectangle valid_area(c.intersect(area));
|
||||
|
||||
rectangle bounding_box;
|
||||
for (unsigned long i = 0; i < polygon.size(); ++i)
|
||||
bounding_box += polygon[i];
|
||||
const rectangle bounding_box = poly.get_rect();
|
||||
|
||||
// Don't do anything if the polygon is totally outside the area we can draw in
|
||||
// right now.
|
||||
@ -558,8 +481,7 @@ namespace dlib
|
||||
|
||||
std::vector<double> left_boundary;
|
||||
std::vector<double> right_boundary;
|
||||
impl::get_convex_polygon_shape(polygon, top, bottom, left_boundary, right_boundary);
|
||||
|
||||
poly.get_convex_shape(top, bottom, left_boundary, right_boundary);
|
||||
|
||||
// draw the polygon row by row
|
||||
for (unsigned long i = top_offset; i < left_boundary.size(); ++i)
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "../pixel.h"
|
||||
#include "../matrix.h"
|
||||
#include "../gui_widgets/fonts.h"
|
||||
#include "../geometry.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace dlib
|
||||
@ -402,7 +403,160 @@ namespace dlib
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
template <typename image_type, typename pixel_type>
|
||||
void fill_convex_polygon (
|
||||
image_type& image,
|
||||
const polygon& poly,
|
||||
const pixel_type& pixel,
|
||||
const bool antialias = true,
|
||||
const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(),
|
||||
std::numeric_limits<long>::max(), std::numeric_limits<long>::max())
|
||||
)
|
||||
{
|
||||
using std::max;
|
||||
using std::min;
|
||||
const rectangle valid_area(get_rect(image).intersect(area));
|
||||
|
||||
const rectangle bounding_box = poly.get_rect();
|
||||
|
||||
// Don't do anything if the polygon is totally outside the area we can draw in
|
||||
// right now.
|
||||
if (bounding_box.intersect(valid_area).is_empty())
|
||||
return;
|
||||
|
||||
rgb_alpha_pixel alpha_pixel;
|
||||
assign_pixel(alpha_pixel, pixel);
|
||||
const unsigned char max_alpha = alpha_pixel.alpha;
|
||||
|
||||
// we will only want to loop over the part of left_boundary that is part of the
|
||||
// valid_area.
|
||||
long top = max(valid_area.top(),bounding_box.top());
|
||||
long bottom = min(valid_area.bottom(),bounding_box.bottom());
|
||||
|
||||
// Since we look at the adjacent rows of boundary information when doing the alpha
|
||||
// blending, we want to make sure we always have some boundary information unless
|
||||
// we are at the absolute edge of the polygon.
|
||||
const long top_offset = (top == bounding_box.top()) ? 0 : 1;
|
||||
const long bottom_offset = (bottom == bounding_box.bottom()) ? 0 : 1;
|
||||
if (top != bounding_box.top())
|
||||
top -= 1;
|
||||
if (bottom != bounding_box.bottom())
|
||||
bottom += 1;
|
||||
|
||||
std::vector<double> left_boundary;
|
||||
std::vector<double> right_boundary;
|
||||
poly.get_convex_shape(top, bottom, left_boundary, right_boundary);
|
||||
|
||||
// draw the polygon row by row
|
||||
for (unsigned long i = top_offset; i < left_boundary.size(); ++i)
|
||||
{
|
||||
long left_x = static_cast<long>(std::ceil(left_boundary[i]));
|
||||
long right_x = static_cast<long>(std::floor(right_boundary[i]));
|
||||
|
||||
left_x = max(left_x, valid_area.left());
|
||||
right_x = min(right_x, valid_area.right());
|
||||
|
||||
if (i < left_boundary.size()-bottom_offset)
|
||||
{
|
||||
// draw the main body of the polygon
|
||||
for (long x = left_x; x <= right_x; ++x)
|
||||
{
|
||||
const long y = i+top;
|
||||
assign_pixel(image(y, x), pixel);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0 || !antialias)
|
||||
continue;
|
||||
|
||||
// Now draw anti-aliased edges so they don't look all pixely.
|
||||
|
||||
// Alpha blend the edges on the left side.
|
||||
double delta = left_boundary[i-1] - left_boundary[i];
|
||||
if (std::abs(delta) <= 1)
|
||||
{
|
||||
if (std::floor(left_boundary[i]) != left_x)
|
||||
{
|
||||
const point p(static_cast<long>(std::floor(left_boundary[i])), i+top);
|
||||
rgb_alpha_pixel temp = alpha_pixel;
|
||||
temp.alpha = max_alpha-static_cast<unsigned char>((left_boundary[i]-p.x())*max_alpha);
|
||||
if (valid_area.contains(p))
|
||||
assign_pixel(image(p.y(), p.x()),temp);
|
||||
}
|
||||
}
|
||||
else if (delta < 0) // on the bottom side
|
||||
{
|
||||
for (long x = static_cast<long>(std::ceil(left_boundary[i-1])); x < left_x; ++x)
|
||||
{
|
||||
const point p(x, i+top);
|
||||
rgb_alpha_pixel temp = alpha_pixel;
|
||||
temp.alpha = static_cast<unsigned char>((x-left_boundary[i-1])/std::abs(delta)*max_alpha);
|
||||
if (valid_area.contains(p))
|
||||
assign_pixel(image(p.y(), p.x()),temp);
|
||||
}
|
||||
}
|
||||
else // on the top side
|
||||
{
|
||||
const long old_left_x = static_cast<long>(std::ceil(left_boundary[i-1]));
|
||||
for (long x = left_x; x < old_left_x; ++x)
|
||||
{
|
||||
const point p(x, i+top-1);
|
||||
rgb_alpha_pixel temp = alpha_pixel;
|
||||
temp.alpha = static_cast<unsigned char>((x-left_boundary[i])/delta*max_alpha);
|
||||
if (valid_area.contains(p))
|
||||
assign_pixel(image(p.y(), p.x()),temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Alpha blend the edges on the right side
|
||||
delta = right_boundary[i-1] - right_boundary[i];
|
||||
if (std::abs(delta) <= 1)
|
||||
{
|
||||
if (std::ceil(right_boundary[i]) != right_x)
|
||||
{
|
||||
const point p(static_cast<long>(std::ceil(right_boundary[i])), i+top);
|
||||
rgb_alpha_pixel temp = alpha_pixel;
|
||||
temp.alpha = max_alpha-static_cast<unsigned char>((p.x()-right_boundary[i])*max_alpha);
|
||||
if (valid_area.contains(p))
|
||||
assign_pixel(image(p.y(), p.x()),temp);
|
||||
}
|
||||
}
|
||||
else if (delta < 0) // on the top side
|
||||
{
|
||||
for (long x = static_cast<long>(std::floor(right_boundary[i-1]))+1; x <= right_x; ++x)
|
||||
{
|
||||
const point p(x, i+top-1);
|
||||
rgb_alpha_pixel temp = alpha_pixel;
|
||||
temp.alpha = static_cast<unsigned char>((right_boundary[i]-x)/std::abs(delta)*max_alpha);
|
||||
if (valid_area.contains(p))
|
||||
assign_pixel(image(p.y(), p.x()),temp);
|
||||
}
|
||||
}
|
||||
else // on the bottom side
|
||||
{
|
||||
const long old_right_x = static_cast<long>(std::floor(right_boundary[i-1]));
|
||||
for (long x = right_x+1; x <= old_right_x; ++x)
|
||||
{
|
||||
const point p(x, i+top);
|
||||
rgb_alpha_pixel temp = alpha_pixel;
|
||||
temp.alpha = static_cast<unsigned char>((right_boundary[i-1]-x)/delta*max_alpha);
|
||||
if (valid_area.contains(p))
|
||||
assign_pixel(image(p.y(), p.x()),temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename image_type>
|
||||
inline void draw_solid_convex_polygon (
|
||||
image_type& image,
|
||||
const polygon& poly
|
||||
) { fill_convex_polygon(image, poly, 0); }
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
typename image_array_type
|
||||
|
@ -190,6 +190,35 @@ namespace dlib
|
||||
- fills the area defined by rect in the given image with the given pixel value.
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <typename image_type, typename pixel_type>
|
||||
void fill_convex_polygon (
|
||||
image_type& image,
|
||||
const polygon& poly,
|
||||
const pixel_type& pixel,
|
||||
const bool antialias = true,
|
||||
const rectangle& area = rectangle(std::numeric_limits<long>::min(), std::numeric_limits<long>::min(),
|
||||
std::numeric_limits<long>::max(), std::numeric_limits<long>::max())
|
||||
);
|
||||
/*!
|
||||
requires
|
||||
- image_type == an image object that implements the interface defined in
|
||||
dlib/image_processing/generic_image.h
|
||||
- pixel_traits<pixel_type> is defined
|
||||
ensures
|
||||
- Interprets the given polygon object as defining a convex polygon shape.
|
||||
In particular, the polygon is given by taking the points and drawing
|
||||
lines between them. That is, imagine drawing a line connecting polygon[i]
|
||||
and polygon[(i+1)%polygon.size()], for all valid i, and then filling in
|
||||
the interior of the polygon. That is what this function does.
|
||||
- When drawing the polygon, only the part of the polygon which overlaps both
|
||||
the given image and area rectangle is drawn.
|
||||
- Uses the given pixel color to draw the polygon.
|
||||
- if (antialias == true) then we draw anti-aliased edges so they don't
|
||||
look all pixely by alpha-blending them with the image.
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
|
@ -895,6 +895,39 @@ namespace
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
void test_polygon()
|
||||
{
|
||||
print_spinner();
|
||||
/* Let's define this triangle
|
||||
* 0 . . . . . . . . . . .
|
||||
* 1 . . . . . . . . .
|
||||
* 2 . . . . . . .
|
||||
* 3 . . . . .
|
||||
* 4 . . .
|
||||
* 5 .
|
||||
* 0 1 2 3 4 5 6 7 8 9 10
|
||||
*/
|
||||
|
||||
const polygon triangle({{0, 0}, {5, 5}, {10, 0}});
|
||||
DLIB_TEST(triangle.area() == 25);
|
||||
|
||||
const rectangle rect = triangle.get_rect();
|
||||
std::vector<double> left_boundary;
|
||||
std::vector<double> right_boundary;
|
||||
triangle.get_convex_shape(rect.top(), rect.bottom(), left_boundary, right_boundary);
|
||||
|
||||
DLIB_TEST(left_boundary.size() == right_boundary.size());
|
||||
|
||||
const long top = rect.top();
|
||||
const long bottom = rect.bottom();
|
||||
for (long y = top, i = 0; y <= bottom; ++y, ++i)
|
||||
DLIB_TEST(left_boundary[y - top] == i);
|
||||
for (long y = top, i = 10; y <= bottom; ++y, --i)
|
||||
DLIB_TEST(right_boundary[y - top] == i);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
class geometry_tester : public tester
|
||||
@ -922,6 +955,7 @@ namespace
|
||||
test_find_similarity_transform<float>();
|
||||
test_find_similarity_transform2<float>();
|
||||
test_line();
|
||||
test_polygon();
|
||||
}
|
||||
} a;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user