New coverage detection for bridges. Includes implementation of ExPolygon::get_trapezoids()
This commit is contained in:
parent
d458a7c4d2
commit
6201aacf88
21 changed files with 358 additions and 27 deletions
|
@ -1,9 +1,9 @@
|
|||
package Slic3r::Layer::BridgeDetector;
|
||||
use Moo;
|
||||
|
||||
use List::Util qw(first sum max);
|
||||
use List::Util qw(first sum max min);
|
||||
use Slic3r::Geometry qw(PI unscale scaled_epsilon rad2deg epsilon);
|
||||
use Slic3r::Geometry::Clipper qw(intersection_pl intersection_ex);
|
||||
use Slic3r::Geometry::Clipper qw(intersection_pl intersection_ex union);
|
||||
|
||||
has 'expolygon' => (is => 'ro', required => 1);
|
||||
has 'lower_slices' => (is => 'rw', required => 1); # ExPolygons or ExPolygonCollection
|
||||
|
@ -153,4 +153,61 @@ sub detect_angle {
|
|||
return $self->angle;
|
||||
}
|
||||
|
||||
sub coverage {
|
||||
my ($self, $angle) = @_;
|
||||
|
||||
if (!defined $angle) {
|
||||
return [] if !defined($angle = $self->detect_angle);
|
||||
}
|
||||
|
||||
# Clone our expolygon and rotate it so that we work with vertical lines.
|
||||
my $expolygon = $self->expolygon->clone;
|
||||
$expolygon->rotate(PI/2 - $angle, [0,0]);
|
||||
|
||||
# Outset the bridge expolygon by half the amount we used for detecting anchors;
|
||||
# we'll use this one to generate our trapezoids and be sure that their vertices
|
||||
# are inside the anchors and not on their contours leading to false negatives.
|
||||
my $grown = $expolygon->offset_ex(+$self->extrusion_width/2);
|
||||
|
||||
# Compute trapezoids according to a vertical orientation
|
||||
my $trapezoids = [ map @{$_->get_trapezoids(PI/2)}, @$grown ];
|
||||
|
||||
# get anchors and rotate them too
|
||||
my $anchors = [ map $_->clone, @{$self->_anchors} ];
|
||||
$_->rotate(PI/2 - $angle, [0,0]) for @$anchors;
|
||||
|
||||
my @covered = (); # polygons
|
||||
foreach my $trapezoid (@$trapezoids) {
|
||||
my @polylines = map $_->as_polyline, @{$trapezoid->lines};
|
||||
my @supported = @{intersection_pl(\@polylines, [map @$_, @$anchors])};
|
||||
|
||||
if (@supported >= 2) {
|
||||
push @covered, $trapezoid;
|
||||
}
|
||||
}
|
||||
|
||||
# merge trapezoids and rotate them back
|
||||
my $coverage = union(\@covered);
|
||||
$_->rotate(-(PI/2 - $angle), [0,0]) for @$coverage;
|
||||
|
||||
# intersect trapezoids with actual bridge area to remove extra margins
|
||||
$coverage = intersection_ex($coverage, [ @{$self->expolygon} ]);
|
||||
|
||||
if (0) {
|
||||
my @lines = map @{$_->lines}, @$trapezoids;
|
||||
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
|
||||
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"coverage_" . rad2deg($angle) . ".svg",
|
||||
expolygons => [$self->expolygon],
|
||||
green_expolygons => $self->_anchors,
|
||||
red_expolygons => $coverage,
|
||||
lines => \@lines,
|
||||
);
|
||||
}
|
||||
|
||||
return $coverage;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
11
t/bridges.t
11
t/bridges.t
|
@ -1,4 +1,4 @@
|
|||
use Test::More tests => 6;
|
||||
use Test::More tests => 12;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
|
@ -7,7 +7,7 @@ BEGIN {
|
|||
use lib "$FindBin::Bin/../lib";
|
||||
}
|
||||
|
||||
use List::Util qw(first);
|
||||
use List::Util qw(first sum);
|
||||
use Slic3r;
|
||||
use Slic3r::Geometry qw(scale epsilon deg2rad rad2deg PI);
|
||||
use Slic3r::Test;
|
||||
|
@ -68,7 +68,10 @@ use Slic3r::Test;
|
|||
}
|
||||
|
||||
sub check_angle {
|
||||
my ($lower, $bridge, $expected, $tolerance) = @_;
|
||||
my ($lower, $bridge, $expected, $tolerance, $expected_coverage) = @_;
|
||||
|
||||
$expected_coverage //= -1;
|
||||
$expected_coverage = $bridge->area if $expected_coverage == -1;
|
||||
|
||||
my $bd = Slic3r::Layer::BridgeDetector->new(
|
||||
expolygon => $bridge,
|
||||
|
@ -78,6 +81,8 @@ sub check_angle {
|
|||
|
||||
$tolerance //= rad2deg($bd->resolution) + epsilon;
|
||||
my $result = $bd->detect_angle;
|
||||
my $coverage = $bd->coverage;
|
||||
is sum(map $_->area, @$coverage), $expected_coverage, 'correct coverage area';
|
||||
|
||||
# our epsilon is equal to the steps used by the bridge detection algorithm
|
||||
###use XXX; YYY [ rad2deg($result), $expected ];
|
||||
|
|
|
@ -24,7 +24,7 @@ my $build = Module::Build::WithXSpp->new(
|
|||
# _GLIBCXX_USE_C99 : to get the long long type for g++
|
||||
# HAS_BOOL : stops Perl/lib/CORE/handy.h from doing "# define bool char" for MSVC
|
||||
# NOGDI : prevents inclusion of wingdi.h which defines functions Polygon() and Polyline() in global namespace
|
||||
extra_compiler_flags => [qw(-D_GLIBCXX_USE_C99 -DHAS_BOOL -DNOGDI -DSLIC3RXS), ($ENV{SLIC3R_DEBUG} ? ' -DSLIC3R_DEBUG -g' : '')],
|
||||
extra_compiler_flags => [qw(-D_GLIBCXX_USE_C99 -DHAS_BOOL -DNOGDI -DSLIC3RXS), ($ENV{SLIC3R_DEBUG} ? ' -DSLIC3R_DEBUG -g -ftemplate-backtrace-limit=0' : '')],
|
||||
|
||||
# Provides extra C typemaps that are auto-merged
|
||||
extra_typemap_modules => {
|
||||
|
|
|
@ -47,7 +47,7 @@ ExPolygon::translate(double x, double y)
|
|||
}
|
||||
|
||||
void
|
||||
ExPolygon::rotate(double angle, Point* center)
|
||||
ExPolygon::rotate(double angle, const Point ¢er)
|
||||
{
|
||||
contour.rotate(angle, center);
|
||||
for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) {
|
||||
|
@ -158,6 +158,24 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
|
|||
intersection(*polylines, *this, *polylines);
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::get_trapezoids(Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp;
|
||||
expp.push_back(*this);
|
||||
boost::polygon::get_trapezoids(*polygons, expp);
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
||||
{
|
||||
ExPolygon clone = *this;
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
clone.get_trapezoids(polygons);
|
||||
for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon)
|
||||
polygon->rotate(-(PI/2 - angle), Point(0,0));
|
||||
}
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
SV*
|
||||
ExPolygon::to_AV() {
|
||||
|
|
|
@ -18,7 +18,7 @@ class ExPolygon
|
|||
operator Polygons() const;
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void rotate(double angle, Point* center);
|
||||
void rotate(double angle, const Point ¢er);
|
||||
double area() const;
|
||||
bool is_valid() const;
|
||||
bool contains_line(const Line* line) const;
|
||||
|
@ -27,6 +27,8 @@ class ExPolygon
|
|||
ExPolygons simplify(double tolerance) const;
|
||||
void simplify(double tolerance, ExPolygons &expolygons) const;
|
||||
void medial_axis(double max_width, double min_width, Polylines* polylines) const;
|
||||
void get_trapezoids(Polygons* polygons) const;
|
||||
void get_trapezoids(Polygons* polygons, double angle) const;
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
void from_SV(SV* poly_sv);
|
||||
|
@ -40,4 +42,108 @@ class ExPolygon
|
|||
|
||||
}
|
||||
|
||||
// start Boost
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct polygon_traits<ExPolygon> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Points::const_iterator iterator_type;
|
||||
typedef Point point_type;
|
||||
|
||||
// Get the begin iterator
|
||||
static inline iterator_type begin_points(const ExPolygon& t) {
|
||||
return t.contour.points.begin();
|
||||
}
|
||||
|
||||
// Get the end iterator
|
||||
static inline iterator_type end_points(const ExPolygon& t) {
|
||||
return t.contour.points.end();
|
||||
}
|
||||
|
||||
// Get the number of sides of the polygon
|
||||
static inline std::size_t size(const ExPolygon& t) {
|
||||
return t.contour.points.size();
|
||||
}
|
||||
|
||||
// Get the winding direction of the polygon
|
||||
static inline winding_direction winding(const ExPolygon& t) {
|
||||
return unknown_winding;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_mutable_traits<ExPolygon> {
|
||||
//expects stl style iterators
|
||||
template <typename iT>
|
||||
static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) {
|
||||
expolygon.contour.points.assign(input_begin, input_end);
|
||||
// skip last point since Boost will set last point = first point
|
||||
expolygon.contour.points.pop_back();
|
||||
return expolygon;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct geometry_concept<ExPolygon> { typedef polygon_with_holes_concept type; };
|
||||
|
||||
template <>
|
||||
struct polygon_with_holes_traits<ExPolygon> {
|
||||
typedef Polygons::const_iterator iterator_holes_type;
|
||||
typedef Polygon hole_type;
|
||||
static inline iterator_holes_type begin_holes(const ExPolygon& t) {
|
||||
return t.holes.begin();
|
||||
}
|
||||
static inline iterator_holes_type end_holes(const ExPolygon& t) {
|
||||
return t.holes.end();
|
||||
}
|
||||
static inline unsigned int size_holes(const ExPolygon& t) {
|
||||
return t.holes.size();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_with_holes_mutable_traits<ExPolygon> {
|
||||
template <typename iT>
|
||||
static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) {
|
||||
t.holes.assign(inputBegin, inputEnd);
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
//first we register CPolygonSet as a polygon set
|
||||
template <>
|
||||
struct geometry_concept<ExPolygons> { typedef polygon_set_concept type; };
|
||||
|
||||
//next we map to the concept through traits
|
||||
template <>
|
||||
struct polygon_set_traits<ExPolygons> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef ExPolygons::const_iterator iterator_type;
|
||||
typedef ExPolygons operator_arg_type;
|
||||
|
||||
static inline iterator_type begin(const ExPolygons& polygon_set) {
|
||||
return polygon_set.begin();
|
||||
}
|
||||
|
||||
static inline iterator_type end(const ExPolygons& polygon_set) {
|
||||
return polygon_set.end();
|
||||
}
|
||||
|
||||
//don't worry about these, just return false from them
|
||||
static inline bool clean(const ExPolygons& polygon_set) { return false; }
|
||||
static inline bool sorted(const ExPolygons& polygon_set) { return false; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_set_mutable_traits<ExPolygons> {
|
||||
template <typename input_iterator_type>
|
||||
static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
|
||||
expolygons.assign(input_begin, input_end);
|
||||
}
|
||||
};
|
||||
} }
|
||||
// end Boost
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,7 +32,7 @@ ExPolygonCollection::translate(double x, double y)
|
|||
}
|
||||
|
||||
void
|
||||
ExPolygonCollection::rotate(double angle, Point* center)
|
||||
ExPolygonCollection::rotate(double angle, const Point ¢er)
|
||||
{
|
||||
for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) {
|
||||
(*it).rotate(angle, center);
|
||||
|
|
|
@ -13,7 +13,7 @@ class ExPolygonCollection
|
|||
operator Polygons() const;
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void rotate(double angle, Point* center);
|
||||
void rotate(double angle, const Point ¢er);
|
||||
bool contains_point(const Point* point) const;
|
||||
void simplify(double tolerance);
|
||||
void convex_hull(Polygon* hull) const;
|
||||
|
|
|
@ -38,7 +38,7 @@ Line::translate(double x, double y)
|
|||
}
|
||||
|
||||
void
|
||||
Line::rotate(double angle, Point* center)
|
||||
Line::rotate(double angle, const Point ¢er)
|
||||
{
|
||||
this->a.rotate(angle, center);
|
||||
this->b.rotate(angle, center);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include <myinit.h>
|
||||
#include "Point.hpp"
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -21,7 +20,7 @@ class Line
|
|||
operator Polyline() const;
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void rotate(double angle, Point* center);
|
||||
void rotate(double angle, const Point ¢er);
|
||||
void reverse();
|
||||
double length() const;
|
||||
Point* midpoint() const;
|
||||
|
@ -48,6 +47,7 @@ typedef std::vector<Line> Lines;
|
|||
}
|
||||
|
||||
// start Boost
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<Line> { typedef segment_concept type; };
|
||||
|
|
|
@ -19,7 +19,7 @@ MultiPoint::translate(double x, double y)
|
|||
}
|
||||
|
||||
void
|
||||
MultiPoint::rotate(double angle, Point* center)
|
||||
MultiPoint::rotate(double angle, const Point ¢er)
|
||||
{
|
||||
for (Points::iterator it = points.begin(); it != points.end(); ++it) {
|
||||
(*it).rotate(angle, center);
|
||||
|
|
|
@ -14,7 +14,7 @@ class MultiPoint
|
|||
Points points;
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void rotate(double angle, Point* center);
|
||||
void rotate(double angle, const Point ¢er);
|
||||
void reverse();
|
||||
Point* first_point() const;
|
||||
virtual Point* last_point() const = 0;
|
||||
|
|
|
@ -34,12 +34,12 @@ Point::translate(double x, double y)
|
|||
}
|
||||
|
||||
void
|
||||
Point::rotate(double angle, Point* center)
|
||||
Point::rotate(double angle, const Point ¢er)
|
||||
{
|
||||
double cur_x = (double)this->x;
|
||||
double cur_y = (double)this->y;
|
||||
this->x = (coord_t)round( (double)center->x + cos(angle) * (cur_x - (double)center->x) - sin(angle) * (cur_y - (double)center->y) );
|
||||
this->y = (coord_t)round( (double)center->y + cos(angle) * (cur_y - (double)center->y) + sin(angle) * (cur_x - (double)center->x) );
|
||||
this->x = (coord_t)round( (double)center.x + cos(angle) * (cur_x - (double)center.x) - sin(angle) * (cur_y - (double)center.y) );
|
||||
this->y = (coord_t)round( (double)center.y + cos(angle) * (cur_y - (double)center.y) + sin(angle) * (cur_x - (double)center.x) );
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <myinit.h>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -27,7 +26,7 @@ class Point
|
|||
std::string wkt() const;
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void rotate(double angle, Point* center);
|
||||
void rotate(double angle, const Point ¢er);
|
||||
bool coincides_with(const Point &point) const;
|
||||
bool coincides_with(const Point* point) const;
|
||||
int nearest_point_index(Points &points) const;
|
||||
|
@ -83,7 +82,21 @@ class Pointf3 : public Pointf
|
|||
}
|
||||
|
||||
// start Boost
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<coord_t> { typedef coordinate_concept type; };
|
||||
|
||||
template <>
|
||||
struct coordinate_traits<coord_t> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef long double area_type;
|
||||
typedef long long manhattan_area_type;
|
||||
typedef unsigned long long unsigned_area_type;
|
||||
typedef long long coordinate_difference;
|
||||
typedef long double coordinate_distance;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct geometry_concept<Point> { typedef point_concept type; };
|
||||
|
||||
|
@ -95,6 +108,23 @@ namespace boost { namespace polygon {
|
|||
return (orient == HORIZONTAL) ? point.x : point.y;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct point_mutable_traits<Point> {
|
||||
typedef coord_t coordinate_type;
|
||||
static inline void set(Point& point, orientation_2d orient, coord_t value) {
|
||||
if (orient == HORIZONTAL)
|
||||
point.x = value;
|
||||
else
|
||||
point.y = value;
|
||||
}
|
||||
static inline Point construct(coord_t x_value, coord_t y_value) {
|
||||
Point retval;
|
||||
retval.x = x_value;
|
||||
retval.y = y_value;
|
||||
return retval;
|
||||
}
|
||||
};
|
||||
} }
|
||||
// end Boost
|
||||
|
||||
|
|
|
@ -41,4 +41,87 @@ class Polygon : public MultiPoint {
|
|||
|
||||
}
|
||||
|
||||
// start Boost
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<Polygon>{ typedef polygon_concept type; };
|
||||
|
||||
template <>
|
||||
struct polygon_traits<Polygon> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Points::const_iterator iterator_type;
|
||||
typedef Point point_type;
|
||||
|
||||
// Get the begin iterator
|
||||
static inline iterator_type begin_points(const Polygon& t) {
|
||||
return t.points.begin();
|
||||
}
|
||||
|
||||
// Get the end iterator
|
||||
static inline iterator_type end_points(const Polygon& t) {
|
||||
return t.points.end();
|
||||
}
|
||||
|
||||
// Get the number of sides of the polygon
|
||||
static inline std::size_t size(const Polygon& t) {
|
||||
return t.points.size();
|
||||
}
|
||||
|
||||
// Get the winding direction of the polygon
|
||||
static inline winding_direction winding(const Polygon& t) {
|
||||
return unknown_winding;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_mutable_traits<Polygon> {
|
||||
// expects stl style iterators
|
||||
template <typename iT>
|
||||
static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) {
|
||||
polygon.points.clear();
|
||||
while (input_begin != input_end) {
|
||||
polygon.points.push_back(Point());
|
||||
boost::polygon::assign(polygon.points.back(), *input_begin);
|
||||
++input_begin;
|
||||
}
|
||||
// skip last point since Boost will set last point = first point
|
||||
polygon.points.pop_back();
|
||||
return polygon;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct geometry_concept<Polygons> { typedef polygon_set_concept type; };
|
||||
|
||||
//next we map to the concept through traits
|
||||
template <>
|
||||
struct polygon_set_traits<Polygons> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Polygons::const_iterator iterator_type;
|
||||
typedef Polygons operator_arg_type;
|
||||
|
||||
static inline iterator_type begin(const Polygons& polygon_set) {
|
||||
return polygon_set.begin();
|
||||
}
|
||||
|
||||
static inline iterator_type end(const Polygons& polygon_set) {
|
||||
return polygon_set.end();
|
||||
}
|
||||
|
||||
//don't worry about these, just return false from them
|
||||
static inline bool clean(const Polygons& polygon_set) { return false; }
|
||||
static inline bool sorted(const Polygons& polygon_set) { return false; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_set_mutable_traits<Polygons> {
|
||||
template <typename input_iterator_type>
|
||||
static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
|
||||
polygons.assign(input_begin, input_end);
|
||||
}
|
||||
};
|
||||
} }
|
||||
// end Boost
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
use List::Util qw(first);
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 21;
|
||||
use Test::More tests => 31;
|
||||
|
||||
use constant PI => 4 * atan2(1, 1);
|
||||
|
||||
|
@ -104,4 +105,32 @@ is $expolygon->area, 100*100-20*20, 'area';
|
|||
is_deeply $collection->[0]->clone->pp, $collection->[0]->pp, 'clone collection item';
|
||||
}
|
||||
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new($square);
|
||||
my $polygons = $expolygon->get_trapezoids(PI/2);
|
||||
is scalar(@$polygons), 1, 'correct number of trapezoids returned';
|
||||
is scalar(@{$polygons->[0]}), 4, 'trapezoid has 4 points';
|
||||
is $polygons->[0]->area, $expolygon->area, 'trapezoid has correct area';
|
||||
}
|
||||
|
||||
{
|
||||
my $polygons = $expolygon->get_trapezoids(PI/2);
|
||||
is scalar(@$polygons), 4, 'correct number of trapezoids returned';
|
||||
|
||||
# trapezoid polygons might have more than 4 points in case of collinear segments
|
||||
$polygons = [ map @{$_->simplify(1)}, @$polygons ];
|
||||
ok !defined(first { @$_ != 4 } @$polygons), 'all trapezoids have 4 points';
|
||||
|
||||
is scalar(grep { $_->area == 40*100 } @$polygons), 2, 'trapezoids have expected area';
|
||||
is scalar(grep { $_->area == 20*40 } @$polygons), 2, 'trapezoids have expected area';
|
||||
}
|
||||
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new([ [0,100],[100,0],[200,0],[300,100],[200,200],[100,200] ]);
|
||||
my $polygons = $expolygon->get_trapezoids(PI/2);
|
||||
is scalar(@$polygons), 3, 'correct number of trapezoids returned';
|
||||
is scalar(grep { $_->area == 100*200/2 } @$polygons), 2, 'trapezoids have expected area';
|
||||
is scalar(grep { $_->area == 100*200 } @$polygons), 1, 'trapezoids have expected area';
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
Polygons simplify_p(double tolerance);
|
||||
Polylines medial_axis(double max_width, double min_width)
|
||||
%code{% THIS->medial_axis(max_width, min_width, &RETVAL); %};
|
||||
Polygons get_trapezoids(double angle)
|
||||
%code{% THIS->get_trapezoids(&RETVAL, angle); %};
|
||||
%{
|
||||
|
||||
ExPolygon*
|
||||
|
@ -49,7 +51,7 @@ ExPolygon::rotate(angle, center_sv)
|
|||
CODE:
|
||||
Point center;
|
||||
center.from_SV_check(center_sv);
|
||||
THIS->rotate(angle, ¢er);
|
||||
THIS->rotate(angle, center);
|
||||
|
||||
%}
|
||||
};
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
%code{% THIS->expolygons.clear(); %};
|
||||
void scale(double factor);
|
||||
void translate(double x, double y);
|
||||
void rotate(double angle, Point* center);
|
||||
void rotate(double angle, Point* center)
|
||||
%code{% THIS->rotate(angle, *center); %};
|
||||
int count()
|
||||
%code{% RETVAL = THIS->expolygons.size(); %};
|
||||
bool contains_point(Point* point);
|
||||
|
|
|
@ -48,7 +48,7 @@ Line::rotate(angle, center_sv)
|
|||
CODE:
|
||||
Point center;
|
||||
center.from_SV_check(center_sv);
|
||||
THIS->rotate(angle, ¢er);
|
||||
THIS->rotate(angle, center);
|
||||
|
||||
bool
|
||||
Line::coincides_with(line_sv)
|
||||
|
|
|
@ -37,7 +37,7 @@ Point::rotate(angle, center_sv)
|
|||
CODE:
|
||||
Point center;
|
||||
center.from_SV_check(center_sv);
|
||||
THIS->rotate(angle, ¢er);
|
||||
THIS->rotate(angle, center);
|
||||
|
||||
bool
|
||||
Point::coincides_with(point_sv)
|
||||
|
|
|
@ -56,7 +56,7 @@ Polygon::rotate(angle, center_sv)
|
|||
CODE:
|
||||
Point center;
|
||||
center.from_SV_check(center_sv);
|
||||
THIS->rotate(angle, ¢er);
|
||||
THIS->rotate(angle, center);
|
||||
|
||||
%}
|
||||
};
|
||||
|
|
|
@ -70,7 +70,7 @@ Polyline::rotate(angle, center_sv)
|
|||
CODE:
|
||||
Point center;
|
||||
center.from_SV_check(center_sv);
|
||||
THIS->rotate(angle, ¢er);
|
||||
THIS->rotate(angle, center);
|
||||
|
||||
Polygons
|
||||
Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3)
|
||||
|
|
Loading…
Reference in a new issue