Ported "avoid crossing perimeters" and bridging unit tests from Perl

to C++.
Further reduced Perl bindings.
Got rid of the ExPolygonCollection wrapper, replaced with ExPolygons.
This commit is contained in:
Vojtech Bubnik 2022-05-04 18:21:08 +02:00
parent a627614b58
commit 576c167bd5
42 changed files with 204 additions and 1006 deletions

View File

@ -9,11 +9,4 @@ sub bounding_box {
return $self->contour->bounding_box;
}
package Slic3r::ExPolygon::Collection;
sub size {
my $self = shift;
return [ Slic3r::Geometry::size_2D([ map @$_, map @$_, @$self ]) ];
}
1;

View File

@ -9,13 +9,6 @@ our @ISA = qw(Exporter);
our @EXPORT_OK = qw(
PI epsilon
angle3points
collinear
dot
line_intersection
normalize
polyline_lines
polygon_is_convex
scale
unscale
scaled_epsilon
@ -26,7 +19,6 @@ our @EXPORT_OK = qw(
chained_path_from
deg2rad
rad2deg
rad2deg_dir
);
use constant PI => 4 * atan2(1, 1);
@ -43,136 +35,6 @@ sub scaled_epsilon () { epsilon / &Slic3r::SCALING_FACTOR }
sub scale ($) { $_[0] / &Slic3r::SCALING_FACTOR }
sub unscale ($) { $_[0] * &Slic3r::SCALING_FACTOR }
# used by geometry.t
sub polyline_lines {
my ($polyline) = @_;
my @points = @$polyline;
return map Slic3r::Line->new(@points[$_, $_+1]), 0 .. $#points-1;
}
# polygon must be simple (non complex) and ccw
sub polygon_is_convex {
my ($points) = @_;
for (my $i = 0; $i <= $#$points; $i++) {
my $angle = angle3points($points->[$i-1], $points->[$i-2], $points->[$i]);
return 0 if $angle < PI;
}
return 1;
}
sub normalize {
my ($line) = @_;
my $len = sqrt( ($line->[X]**2) + ($line->[Y]**2) + ($line->[Z]**2) )
or return [0, 0, 0]; # to avoid illegal division by zero
return [ map $_ / $len, @$line ];
}
# 2D dot product
# used by 3DScene.pm
sub dot {
my ($u, $v) = @_;
return $u->[X] * $v->[X] + $u->[Y] * $v->[Y];
}
sub line_intersection {
my ($line1, $line2, $require_crossing) = @_;
$require_crossing ||= 0;
my $intersection = _line_intersection(map @$_, @$line1, @$line2);
return (ref $intersection && $intersection->[1] == $require_crossing)
? $intersection->[0]
: undef;
}
# Used by test cases.
sub collinear {
my ($line1, $line2, $require_overlapping) = @_;
my $intersection = _line_intersection(map @$_, @$line1, @$line2);
return 0 unless !ref($intersection)
&& ($intersection eq 'parallel collinear'
|| ($intersection eq 'parallel vertical' && abs($line1->[A][X] - $line2->[A][X]) < epsilon));
if ($require_overlapping) {
my @box_a = bounding_box([ $line1->[0], $line1->[1] ]);
my @box_b = bounding_box([ $line2->[0], $line2->[1] ]);
return 0 unless bounding_box_intersect( 2, @box_a, @box_b );
}
return 1;
}
sub _line_intersection {
my ( $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3 ) = @_;
my ($x, $y); # The as-yet-undetermined intersection point.
my $dy10 = $y1 - $y0; # dyPQ, dxPQ are the coordinate differences
my $dx10 = $x1 - $x0; # between the points P and Q.
my $dy32 = $y3 - $y2;
my $dx32 = $x3 - $x2;
my $dy10z = abs( $dy10 ) < epsilon; # Is the difference $dy10 "zero"?
my $dx10z = abs( $dx10 ) < epsilon;
my $dy32z = abs( $dy32 ) < epsilon;
my $dx32z = abs( $dx32 ) < epsilon;
my $dyx10; # The slopes.
my $dyx32;
$dyx10 = $dy10 / $dx10 unless $dx10z;
$dyx32 = $dy32 / $dx32 unless $dx32z;
# Now we know all differences and the slopes;
# we can detect horizontal/vertical special cases.
# E.g., slope = 0 means a horizontal line.
unless ( defined $dyx10 or defined $dyx32 ) {
return "parallel vertical";
}
elsif ( $dy10z and not $dy32z ) { # First line horizontal.
$y = $y0;
$x = $x2 + ( $y - $y2 ) * $dx32 / $dy32;
}
elsif ( not $dy10z and $dy32z ) { # Second line horizontal.
$y = $y2;
$x = $x0 + ( $y - $y0 ) * $dx10 / $dy10;
}
elsif ( $dx10z and not $dx32z ) { # First line vertical.
$x = $x0;
$y = $y2 + $dyx32 * ( $x - $x2 );
}
elsif ( not $dx10z and $dx32z ) { # Second line vertical.
$x = $x2;
$y = $y0 + $dyx10 * ( $x - $x0 );
}
elsif ( abs( $dyx10 - $dyx32 ) < epsilon ) {
# The slopes are suspiciously close to each other.
# Either we have parallel collinear or just parallel lines.
# The bounding box checks have already weeded the cases
# "parallel horizontal" and "parallel vertical" away.
my $ya = $y0 - $dyx10 * $x0;
my $yb = $y2 - $dyx32 * $x2;
return "parallel collinear" if abs( $ya - $yb ) < epsilon;
return "parallel";
}
else {
# None of the special cases matched.
# We have a "honest" line intersection.
$x = ($y2 - $y0 + $dyx10*$x0 - $dyx32*$x2)/($dyx10 - $dyx32);
$y = $y0 + $dyx10 * ($x - $x0);
}
my $h10 = $dx10 ? ($x - $x0) / $dx10 : ($dy10 ? ($y - $y0) / $dy10 : 1);
my $h32 = $dx32 ? ($x - $x2) / $dx32 : ($dy32 ? ($y - $y2) / $dy32 : 1);
return [Slic3r::Point->new($x, $y), $h10 >= 0 && $h10 <= 1 && $h32 >= 0 && $h32 <= 1];
}
# 2D
sub bounding_box {
my ($points) = @_;
@ -199,36 +61,4 @@ sub size_2D {
);
}
# Used by sub collinear, which is used by test cases.
# bounding_box_intersect($d, @a, @b)
# Return true if the given bounding boxes @a and @b intersect
# in $d dimensions. Used by sub collinear.
sub bounding_box_intersect {
my ( $d, @bb ) = @_; # Number of dimensions and box coordinates.
my @aa = splice( @bb, 0, 2 * $d ); # The first box.
# (@bb is the second one.)
# Must intersect in all dimensions.
for ( my $i_min = 0; $i_min < $d; $i_min++ ) {
my $i_max = $i_min + $d; # The index for the maximum.
return 0 if ( $aa[ $i_max ] + epsilon ) < $bb[ $i_min ];
return 0 if ( $bb[ $i_max ] + epsilon ) < $aa[ $i_min ];
}
return 1;
}
# Used by test cases.
# this assumes a CCW rotation from $p2 to $p3 around $p1
sub angle3points {
my ($p1, $p2, $p3) = @_;
# p1 is the center
my $angle = atan2($p2->[X] - $p1->[X], $p2->[Y] - $p1->[Y])
- atan2($p3->[X] - $p1->[X], $p3->[Y] - $p1->[Y]);
# we only want to return only positive angles
return $angle <= 0 ? $angle + 2*PI() : $angle;
}
1;

View File

@ -40,8 +40,6 @@ set(SLIC3R_SOURCES
enum_bitmask.hpp
ExPolygon.cpp
ExPolygon.hpp
ExPolygonCollection.cpp
ExPolygonCollection.hpp
Extruder.cpp
Extruder.hpp
ExtrusionEntity.cpp

View File

@ -240,7 +240,7 @@ TResult clipper_union(
// Perform union of input polygons using the positive rule, convert to ExPolygons.
//FIXME is there any benefit of not doing the boolean / using pftEvenOdd?
ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool do_union)
inline ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool do_union)
{
return PolyTreeToExPolygons(clipper_union<ClipperLib::PolyTree>(input, do_union ? ClipperLib::pftNonZero : ClipperLib::pftEvenOdd));
}
@ -438,7 +438,7 @@ Slic3r::Polygons offset(const Slic3r::SurfacesPtr &surfaces, const float delta,
{ return to_polygons(expolygons_offset(surfaces, delta, joinType, miterLimit)); }
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit)
//FIXME one may spare one Clipper Union call.
{ return ClipperPaths_to_Slic3rExPolygons(expolygon_offset(expolygon, delta, joinType, miterLimit)); }
{ return ClipperPaths_to_Slic3rExPolygons(expolygon_offset(expolygon, delta, joinType, miterLimit), /* do union */ false); }
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit)
{ return PolyTreeToExPolygons(expolygons_offset_pt(expolygons, delta, joinType, miterLimit)); }
Slic3r::ExPolygons offset_ex(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType, double miterLimit)
@ -713,6 +713,8 @@ Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonsProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip)

View File

@ -1,17 +1,27 @@
#ifndef slic3r_ClipperUtils_hpp_
#define slic3r_ClipperUtils_hpp_
//#define SLIC3R_USE_CLIPPER2
#include "libslic3r.h"
#include "clipper.hpp"
#include "ExPolygon.hpp"
#include "Polygon.hpp"
#include "Surface.hpp"
#ifdef SLIC3R_USE_CLIPPER2
#include <clipper2.clipper.h>
#else /* SLIC3R_USE_CLIPPER2 */
#include "clipper.hpp"
// import these wherever we're included
using Slic3r::ClipperLib::jtMiter;
using Slic3r::ClipperLib::jtRound;
using Slic3r::ClipperLib::jtSquare;
#endif /* SLIC3R_USE_CLIPPER2 */
namespace Slic3r {
static constexpr const float ClipperSafetyOffset = 10.f;
@ -298,9 +308,6 @@ namespace ClipperUtils {
};
}
// Perform union of input polygons using the non-zero rule, convert to ExPolygons.
ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool do_union = false);
// offset Polygons
// Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better.
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
@ -429,6 +436,7 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);

View File

@ -136,11 +136,6 @@ void EdgeGrid::Grid::create(const ExPolygons &expolygons, coord_t resolution)
create_from_m_contours(resolution);
}
void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resolution)
{
create(expolygons.expolygons, resolution);
}
// m_contours has been initialized. Now fill in the edge grid.
void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
{

View File

@ -7,7 +7,6 @@
#include "Point.hpp"
#include "BoundingBox.hpp"
#include "ExPolygon.hpp"
#include "ExPolygonCollection.hpp"
namespace Slic3r {
namespace EdgeGrid {
@ -112,7 +111,6 @@ public:
void create(const std::vector<Points> &polygons, coord_t resolution) { this->create(polygons, resolution, false); }
void create(const ExPolygon &expoly, coord_t resolution);
void create(const ExPolygons &expolygons, coord_t resolution);
void create(const ExPolygonCollection &expolygons, coord_t resolution);
const std::vector<Contour>& contours() const { return m_contours; }
@ -123,7 +121,6 @@ public:
bool intersect(const Polygons &polygons) { for (size_t i = 0; i < polygons.size(); ++ i) if (intersect(polygons[i])) return true; return false; }
bool intersect(const ExPolygon &expoly) { if (intersect(expoly.contour)) return true; for (size_t i = 0; i < expoly.holes.size(); ++ i) if (intersect(expoly.holes[i])) return true; return false; }
bool intersect(const ExPolygons &expolygons) { for (size_t i = 0; i < expolygons.size(); ++ i) if (intersect(expolygons[i])) return true; return false; }
bool intersect(const ExPolygonCollection &expolygons) { return intersect(expolygons.expolygons); }
// Test, whether a point is inside a contour.
bool inside(const Point &pt);
@ -391,7 +388,7 @@ protected:
// Referencing the source contours.
// This format allows one to work with any Slic3r fixed point contour format
// (Polygon, ExPolygon, ExPolygonCollection etc).
// (Polygon, ExPolygon, ExPolygons etc).
std::vector<Contour> m_contours;
// Referencing a contour and a line segment of m_contours.

View File

@ -9,7 +9,7 @@
namespace Slic3r {
class ExPolygon;
typedef std::vector<ExPolygon> ExPolygons;
using ExPolygons = std::vector<ExPolygon>;
class ExPolygon
{

View File

@ -1,136 +0,0 @@
#include "ExPolygonCollection.hpp"
#include "Geometry/ConvexHull.hpp"
#include "BoundingBox.hpp"
namespace Slic3r {
ExPolygonCollection::ExPolygonCollection(const ExPolygon &expolygon)
{
this->expolygons.push_back(expolygon);
}
ExPolygonCollection::operator Points() const
{
Points points;
Polygons pp = (Polygons)*this;
for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) {
for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point)
points.push_back(*point);
}
return points;
}
ExPolygonCollection::operator Polygons() const
{
Polygons polygons;
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
polygons.push_back(it->contour);
for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) {
polygons.push_back(*ith);
}
}
return polygons;
}
ExPolygonCollection::operator ExPolygons&()
{
return this->expolygons;
}
void
ExPolygonCollection::scale(double factor)
{
for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) {
(*it).scale(factor);
}
}
void
ExPolygonCollection::translate(double x, double y)
{
for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) {
(*it).translate(x, y);
}
}
void
ExPolygonCollection::rotate(double angle, const Point &center)
{
for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) {
(*it).rotate(angle, center);
}
}
template <class T>
bool ExPolygonCollection::contains(const T &item) const
{
for (const ExPolygon &poly : this->expolygons)
if (poly.contains(item))
return true;
return false;
}
template bool ExPolygonCollection::contains<Point>(const Point &item) const;
template bool ExPolygonCollection::contains<Line>(const Line &item) const;
template bool ExPolygonCollection::contains<Polyline>(const Polyline &item) const;
bool
ExPolygonCollection::contains_b(const Point &point) const
{
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
if (it->contains_b(point)) return true;
}
return false;
}
void
ExPolygonCollection::simplify(double tolerance)
{
ExPolygons expp;
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
it->simplify(tolerance, &expp);
}
this->expolygons = expp;
}
Polygon
ExPolygonCollection::convex_hull() const
{
Points pp;
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it)
pp.insert(pp.end(), it->contour.points.begin(), it->contour.points.end());
return Slic3r::Geometry::convex_hull(pp);
}
Lines
ExPolygonCollection::lines() const
{
Lines lines;
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
Lines ex_lines = it->lines();
lines.insert(lines.end(), ex_lines.begin(), ex_lines.end());
}
return lines;
}
Polygons
ExPolygonCollection::contours() const
{
Polygons contours;
contours.reserve(this->expolygons.size());
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it)
contours.push_back(it->contour);
return contours;
}
void
ExPolygonCollection::append(const ExPolygons &expp)
{
this->expolygons.insert(this->expolygons.end(), expp.begin(), expp.end());
}
BoundingBox get_extents(const ExPolygonCollection &expolygon)
{
return get_extents(expolygon.expolygons);
}
}

View File

@ -1,41 +0,0 @@
#ifndef slic3r_ExPolygonCollection_hpp_
#define slic3r_ExPolygonCollection_hpp_
#include "libslic3r.h"
#include "ExPolygon.hpp"
#include "Line.hpp"
#include "Polyline.hpp"
namespace Slic3r {
class ExPolygonCollection;
typedef std::vector<ExPolygonCollection> ExPolygonCollections;
class ExPolygonCollection
{
public:
ExPolygons expolygons;
ExPolygonCollection() {}
explicit ExPolygonCollection(const ExPolygon &expolygon);
explicit ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {}
explicit operator Points() const;
explicit operator Polygons() const;
explicit operator ExPolygons&();
void scale(double factor);
void translate(double x, double y);
void rotate(double angle, const Point &center);
template <class T> bool contains(const T &item) const;
bool contains_b(const Point &point) const;
void simplify(double tolerance);
Polygon convex_hull() const;
Lines lines() const;
Polygons contours() const;
void append(const ExPolygons &expolygons);
};
extern BoundingBox get_extents(const ExPolygonCollection &expolygon);
}
#endif

View File

@ -1,6 +1,6 @@
#include "ExtrusionEntity.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "ExPolygonCollection.hpp"
#include "ExPolygon.hpp"
#include "ClipperUtils.hpp"
#include "Extruder.hpp"
#include "Flow.hpp"
@ -12,14 +12,14 @@
namespace Slic3r {
void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const
{
this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection.expolygons), retval);
this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection), retval);
}
void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
void ExtrusionPath::subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const
{
this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection.expolygons), retval);
this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection), retval);
}
void ExtrusionPath::clip_end(double distance)

View File

@ -11,7 +11,8 @@
namespace Slic3r {
class ExPolygonCollection;
class ExPolygon;
using ExPolygons = std::vector<ExPolygon>;
class ExtrusionEntityCollection;
class Extruder;
@ -144,12 +145,12 @@ public:
size_t size() const { return this->polyline.size(); }
bool empty() const { return this->polyline.empty(); }
bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
// Produce a list of extrusion paths into retval by clipping this path by ExPolygons.
// Currently not used.
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
// Produce a list of extrusion paths into retval by removing parts of this path by ExPolygonCollection.
void intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const;
// Produce a list of extrusion paths into retval by removing parts of this path by ExPolygons.
// Currently not used.
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
void subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance);
void simplify(double tolerance);
double length() const override;

View File

@ -3063,7 +3063,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
if (role == erSupportMaterial) {
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
//FIXME support_layer->support_islands.contains should use some search structure!
if (support_layer != NULL && support_layer->support_islands.contains(travel))
if (support_layer != NULL && ! intersection_pl(travel, support_layer->support_islands).empty())
// skip retraction if this is a travel move inside a support material island
//FIXME not retracting over a long path may cause oozing, which in turn may result in missing material
// at the end of the extrusion path!

View File

@ -50,13 +50,6 @@ bool contains(const std::vector<T> &vector, const Point &point)
}
template bool contains(const ExPolygons &vector, const Point &point);
double rad2deg_dir(double angle)
{
angle = (angle < PI) ? (-angle + PI/2.0) : (angle + PI/2.0);
if (angle < 0) angle += PI;
return rad2deg(angle);
}
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
{
Polygons pp;

View File

@ -291,7 +291,6 @@ 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);
template<typename T> T rad2deg(T angle) { return T(180.0) * angle / T(PI); }
double rad2deg_dir(double angle);
template<typename T> constexpr T deg2rad(const T angle) { return T(PI) * angle / T(180.0); }
template<typename T> T angle_to_0_2PI(T angle)
{

View File

@ -104,6 +104,17 @@ Polygon convex_hull(const Polygons &polygons)
return convex_hull(std::move(pp));
}
Polygon convex_hull(const ExPolygons &expolygons)
{
Points pp;
size_t sz = 0;
for (const auto &expoly : expolygons)
sz += expoly.contour.size();
pp.reserve(sz);
for (const auto &expoly : expolygons)
pp.insert(pp.end(), expoly.contour.points.begin(), expoly.contour.points.end());
return convex_hull(pp);
}
namespace rotcalip {

View File

@ -9,6 +9,7 @@ namespace Geometry {
Pointf3s convex_hull(Pointf3s points);
Polygon convex_hull(Points points);
Polygon convex_hull(const Polygons &polygons);
Polygon convex_hull(const ExPolygons &expolygons);
// Returns true if the intersection of the two convex polygons A and B
// is not an empty set.

View File

@ -5,10 +5,11 @@
#include "Flow.hpp"
#include "SurfaceCollection.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "ExPolygonCollection.hpp"
namespace Slic3r {
class ExPolygon;
using ExPolygons = std::vector<ExPolygon>;
class Layer;
using LayerPtrs = std::vector<Layer*>;
class LayerRegion;
@ -191,7 +192,7 @@ class SupportLayer : public Layer
public:
// Polygons covered by the supports: base, interface and contact areas.
// Used to suppress retraction if moving for a support extrusion over these support_islands.
ExPolygonCollection support_islands;
ExPolygons support_islands;
// Extrusion paths for the support base and for the support interface and contacts.
ExtrusionEntityCollection support_fills;

View File

@ -2,7 +2,6 @@
#include "Polyline.hpp"
#include "Exception.hpp"
#include "ExPolygon.hpp"
#include "ExPolygonCollection.hpp"
#include "Line.hpp"
#include "Polygon.hpp"
#include <iostream>

View File

@ -1,17 +1,14 @@
//#include "igl/random_points_on_mesh.h"
//#include "igl/AABB.h"
#include <tbb/parallel_for.h>
#include "SupportPointGenerator.hpp"
#include "Concurrency.hpp"
#include "Geometry/ConvexHull.hpp"
#include "Model.hpp"
#include "ExPolygon.hpp"
#include "SVG.hpp"
#include "Point.hpp"
#include "ClipperUtils.hpp"
#include "Tesselate.hpp"
#include "ExPolygonCollection.hpp"
#include "MinAreaBoundingBox.hpp"
#include "libslic3r.h"
@ -550,7 +547,7 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure
// auto bb = get_extents(islands);
if (flags & icfIsNew) {
auto chull = ExPolygonCollection{islands}.convex_hull();
auto chull = Geometry::convex_hull(islands);
auto rotbox = MinAreaBoundigBox{chull, MinAreaBoundigBox::pcConvex};
Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())};

View File

@ -4283,7 +4283,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
}
if (! polys.empty())
expolygons_append(support_layer.support_islands.expolygons, union_ex(polys));
expolygons_append(support_layer.support_islands, union_ex(polys));
} // for each support_layer_id
});

View File

@ -1,93 +0,0 @@
use Test::More;
use strict;
use warnings;
plan tests => 34;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use Slic3r;
use Slic3r::Geometry qw(rad2deg_dir angle3points PI);
#==========================================================
{
is line_atan([ [0, 0], [10, 0] ]), (0), 'E atan2';
is line_atan([ [10, 0], [0, 0] ]), (PI), 'W atan2';
is line_atan([ [0, 0], [0, 10] ]), (PI/2), 'N atan2';
is line_atan([ [0, 10], [0, 0] ]), -(PI/2), 'S atan2';
is line_atan([ [10, 10], [0, 0] ]), -(PI*3/4), 'SW atan2';
is line_atan([ [0, 0], [10, 10] ]), (PI*1/4), 'NE atan2';
is line_atan([ [0, 10], [10, 0] ]), -(PI*1/4), 'SE atan2';
is line_atan([ [10, 0], [0, 10] ]), (PI*3/4), 'NW atan2';
}
#==========================================================
{
is line_orientation([ [0, 0], [10, 0] ]), (0), 'E orientation';
is line_orientation([ [0, 0], [0, 10] ]), (PI/2), 'N orientation';
is line_orientation([ [10, 0], [0, 0] ]), (PI), 'W orientation';
is line_orientation([ [0, 10], [0, 0] ]), (PI*3/2), 'S orientation';
is line_orientation([ [0, 0], [10, 10] ]), (PI*1/4), 'NE orientation';
is line_orientation([ [10, 0], [0, 10] ]), (PI*3/4), 'NW orientation';
is line_orientation([ [10, 10], [0, 0] ]), (PI*5/4), 'SW orientation';
is line_orientation([ [0, 10], [10, 0] ]), (PI*7/4), 'SE orientation';
}
#==========================================================
{
is line_direction([ [0, 0], [10, 0] ]), (0), 'E direction';
is line_direction([ [10, 0], [0, 0] ]), (0), 'W direction';
is line_direction([ [0, 0], [0, 10] ]), (PI/2), 'N direction';
is line_direction([ [0, 10], [0, 0] ]), (PI/2), 'S direction';
is line_direction([ [10, 10], [0, 0] ]), (PI*1/4), 'SW direction';
is line_direction([ [0, 0], [10, 10] ]), (PI*1/4), 'NE direction';
is line_direction([ [0, 10], [10, 0] ]), (PI*3/4), 'SE direction';
is line_direction([ [10, 0], [0, 10] ]), (PI*3/4), 'NW direction';
}
#==========================================================
{
is rad2deg_dir(0), 90, 'E (degrees)';
is rad2deg_dir(PI), 270, 'W (degrees)';
is rad2deg_dir(PI/2), 0, 'N (degrees)';
is rad2deg_dir(-(PI/2)), 180, 'S (degrees)';
is rad2deg_dir(PI*1/4), 45, 'NE (degrees)';
is rad2deg_dir(PI*3/4), 135, 'NW (degrees)';
is rad2deg_dir(PI/6), 60, '30°';
is rad2deg_dir(PI/6*2), 30, '60°';
}
#==========================================================
{
is angle3points([0,0], [10,0], [0,10]), PI/2, 'CW angle3points';
is angle3points([0,0], [0,10], [10,0]), PI/2*3, 'CCW angle3points';
}
#==========================================================
sub line_atan {
my ($l) = @_;
return Slic3r::Line->new(@$l)->atan2_;
}
sub line_orientation {
my ($l) = @_;
return Slic3r::Line->new(@$l)->orientation;
}
sub line_direction {
my ($l) = @_;
return Slic3r::Line->new(@$l)->direction;
}

View File

@ -1,22 +0,0 @@
use Test::More tests => 1;
use strict;
use warnings;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use List::Util qw(first sum);
use Slic3r;
use Slic3r::Test;
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('avoid_crossing_perimeters', 2);
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
ok my $gcode = Slic3r::Test::gcode($print), "no crash with avoid_crossing_perimeters and multiple objects";
}
__END__

View File

@ -1,137 +0,0 @@
use Test::More tests => 16;
use strict;
use warnings;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use List::Util qw(first sum);
use Slic3r;
use Slic3r::Geometry qw(scale epsilon deg2rad rad2deg);
use Slic3r::Test;
{
my $test = sub {
my ($bridge_size, $rotate, $expected_angle, $tolerance) = @_;
my ($x, $y) = @$bridge_size;
my $lower = Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([-2,-2], [$x+2,-2], [$x+2,$y+2], [-2,$y+2]),
Slic3r::Polygon->new_scale([0,0], [0,$y], [$x,$y], [$x,0]),
);
$lower->translate(scale 20, scale 20); # avoid negative coordinates for easier SVG preview
$lower->rotate(deg2rad($rotate), [$x/2,$y/2]);
my $bridge = $lower->[1]->clone;
$bridge->reverse;
$bridge = Slic3r::ExPolygon->new($bridge);
ok check_angle([$lower], $bridge, $expected_angle, $tolerance), 'correct bridge angle for O-shaped overhang';
};
$test->([20,10], 0, 90);
$test->([10,20], 0, 0);
$test->([20,10], 45, 135, 20);
$test->([20,10], 135, 45, 20);
}
{
my $bridge = Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([0,0], [20,0], [20,10], [0,10]),
);
my $lower = [
Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([-2,0], [0,0], [0,10], [-2,10]),
),
];
$_->translate(scale 20, scale 20) for $bridge, @$lower; # avoid negative coordinates for easier SVG preview
$lower->[1] = $lower->[0]->clone;
$lower->[1]->translate(scale 22, 0);
ok check_angle($lower, $bridge, 0), 'correct bridge angle for two-sided bridge';
}
{
my $bridge = Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([0,0], [20,0], [10,10], [0,10]),
);
my $lower = [
Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([0,0], [0,10], [10,10], [10,12], [-2,12], [-2,-2], [22,-2], [22,0]),
),
];
$_->translate(scale 20, scale 20) for $bridge, @$lower; # avoid negative coordinates for easier SVG preview
ok check_angle($lower, $bridge, 135), 'correct bridge angle for C-shaped overhang';
}
{
my $bridge = Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([10,10],[20,10],[20,20], [10,20]),
);
my $lower = [
Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([10,10],[10,20],[20,20],[30,30],[0,30],[0,0]),
),
];
$_->translate(scale 20, scale 20) for $bridge, @$lower; # avoid negative coordinates for easier SVG preview
ok check_angle($lower, $bridge, 45, undef, $bridge->area/2), 'correct bridge angle for square overhang with L-shaped anchors';
}
sub check_angle {
my ($lower, $bridge, $expected, $tolerance, $expected_coverage) = @_;
if (ref($lower) eq 'ARRAY') {
$lower = Slic3r::ExPolygon::Collection->new(@$lower);
}
$expected_coverage //= -1;
$expected_coverage = $bridge->area if $expected_coverage == -1;
my $bd = Slic3r::BridgeDetector->new($bridge, $lower, scale 0.5);
$tolerance //= rad2deg($bd->resolution) + epsilon;
$bd->detect_angle;
my $result = $bd->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 ];
# returned value must be non-negative, check for that too
my $delta=rad2deg($result) - $expected;
$delta-=180 if $delta>=180 - epsilon;
return defined $result && $result>=0 && abs($delta) < $tolerance;
}
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('top_solid_layers', 0); # to prevent bridging on sparse infill
$config->set('bridge_speed', 99);
my $print = Slic3r::Test::init_print('bridge', config => $config);
my %extrusions = (); # angle => length
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
my ($self, $cmd, $args, $info) = @_;
if ($cmd eq 'G1' && ($args->{F} // $self->F)/60 == $config->bridge_speed) {
my $line = Slic3r::Line->new_scale(
[ $self->X, $self->Y ],
[ $info->{new_X}, $info->{new_Y} ],
);
my $angle = $line->direction;
$extrusions{$angle} //= 0;
$extrusions{$angle} += $line->length;
}
});
ok !!%extrusions, "bridge is generated";
my ($main_angle) = sort { $extrusions{$b} <=> $extrusions{$a} } keys %extrusions;
is $main_angle, 0, "bridge has the expected direction";
}
__END__

View File

@ -1,87 +0,0 @@
use Test::More;
use strict;
use warnings;
plan tests => 11;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use Slic3r;
use Slic3r::Geometry qw(collinear);
#==========================================================
{
my @lines = (
Slic3r::Line->new([0,4], [4,2]),
Slic3r::Line->new([2,3], [8,0]),
Slic3r::Line->new([6,1], [8,0]),
);
is collinear($lines[0], $lines[1]), 1, 'collinear';
is collinear($lines[1], $lines[2]), 1, 'collinear';
is collinear($lines[0], $lines[2]), 1, 'collinear';
}
#==========================================================
{
# horizontal
my @lines = (
Slic3r::Line->new([0,1], [5,1]),
Slic3r::Line->new([2,1], [8,1]),
);
is collinear($lines[0], $lines[1]), 1, 'collinear';
}
#==========================================================
{
# vertical
my @lines = (
Slic3r::Line->new([1,0], [1,5]),
Slic3r::Line->new([1,2], [1,8]),
);
is collinear($lines[0], $lines[1]), 1, 'collinear';
}
#==========================================================
{
# non overlapping
my @lines = (
Slic3r::Line->new([0,1], [5,1]),
Slic3r::Line->new([7,1], [10,1]),
);
is collinear($lines[0], $lines[1], 1), 0, 'non overlapping';
is collinear($lines[0], $lines[1], 0), 1, 'overlapping';
}
#==========================================================
{
# with one common point
my @lines = (
Slic3r::Line->new([0,4], [4,2]),
Slic3r::Line->new([4,2], [8,0]),
);
is collinear($lines[0], $lines[1], 1), 1, 'one common point';
is collinear($lines[0], $lines[1], 0), 1, 'one common point';
}
#==========================================================
{
# not collinear
my @lines = (
Slic3r::Line->new([290000000,690525600], [285163380,684761540]),
Slic3r::Line->new([285163380,684761540], [193267599,575244400]),
);
is collinear($lines[0], $lines[1], 0), 0, 'not collinear';
is collinear($lines[0], $lines[1], 1), 0, 'not collinear';
}
#==========================================================

View File

@ -2,7 +2,7 @@ use Test::More;
use strict;
use warnings;
plan tests => 26;
plan tests => 11;
BEGIN {
use FindBin;
@ -11,7 +11,7 @@ BEGIN {
}
use Slic3r;
use Slic3r::Geometry qw(PI polygon_is_convex
use Slic3r::Geometry qw(PI
chained_path_from epsilon scale);
{
@ -27,18 +27,6 @@ use Slic3r::Geometry qw(PI polygon_is_convex
#==========================================================
my $line1 = [ [5, 15], [30, 15] ];
my $line2 = [ [10, 20], [10, 10] ];
is_deeply Slic3r::Geometry::line_intersection($line1, $line2, 1)->arrayref, [10, 15], 'line_intersection';
#==========================================================
$line1 = [ [73.6310778185108/0.0000001, 371.74239268924/0.0000001], [73.6310778185108/0.0000001, 501.74239268924/0.0000001] ];
$line2 = [ [75/0.0000001, 437.9853/0.0000001], [62.7484/0.0000001, 440.4223/0.0000001] ];
isnt Slic3r::Geometry::line_intersection($line1, $line2, 1), undef, 'line_intersection';
#==========================================================
my $polygons = [
Slic3r::Polygon->new( # contour, ccw
[45919000, 515273900], [14726100, 461246400], [14726100, 348753500], [33988700, 315389800],
@ -57,54 +45,6 @@ my $polygons = [
),
];
#==========================================================
{
my $p1 = [10, 10];
my $p2 = [10, 20];
my $p3 = [10, 30];
my $p4 = [20, 20];
my $p5 = [0, 20];
is Slic3r::Geometry::angle3points($p2, $p3, $p1), PI(), 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p3, $p4), PI()/2*3, 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p4, $p3), PI()/2, 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2, 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p5), PI()/2*3, 'angle3points';
}
{
my $p1 = [30, 30];
my $p2 = [20, 20];
my $p3 = [10, 10];
my $p4 = [30, 10];
is Slic3r::Geometry::angle3points($p2, $p1, $p3), PI(), 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p4), PI()/2*3, 'angle3points';
is Slic3r::Geometry::angle3points($p2, $p1, $p1), 2*PI(), 'angle3points';
}
#==========================================================
{
my $cw_square = [ [0,0], [0,10], [10,10], [10,0] ];
is polygon_is_convex($cw_square), 0, 'cw square is not convex';
is polygon_is_convex([ reverse @$cw_square ]), 1, 'ccw square is convex';
my $convex1 = [ [0,0], [10,0], [10,10], [0,10], [0,6], [4,6], [4,4], [0,4] ];
is polygon_is_convex($convex1), 0, 'concave polygon';
}
#==========================================================
{
my $polyline = Slic3r::Polyline->new([0, 0], [10, 0], [20, 0]);
is_deeply [ map $_->pp, @{$polyline->lines} ], [
[ [0, 0], [10, 0] ],
[ [10, 0], [20, 0] ],
], 'polyline_lines';
}
#==========================================================

View File

@ -1,6 +1,8 @@
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests
${_TEST_NAME}_tests.cpp
test_avoid_crossing_perimeters.cpp
test_bridges.cpp
test_data.cpp
test_data.hpp
test_extrusion_entity.cpp

View File

@ -0,0 +1,16 @@
#include <catch2/catch.hpp>
#include "test_data.hpp"
using namespace Slic3r;
SCENARIO("Avoid crossing perimeters", "[AvoidCrossingPerimeters]") {
WHEN("Two 20mm cubes sliced") {
std::string gcode = Slic3r::Test::slice(
{ Slic3r::Test::TestMesh::cube_20x20x20, Slic3r::Test::TestMesh::cube_20x20x20 },
{ { "avoid_crossing_perimeters", true } });
THEN("gcode not empty") {
REQUIRE(! gcode.empty());
}
}
}

View File

@ -0,0 +1,133 @@
#include <catch2/catch.hpp>
#include <libslic3r/BridgeDetector.hpp>
#include <libslic3r/Geometry.hpp>
#include "test_data.hpp"
using namespace Slic3r;
SCENARIO("Bridge detector", "[Bridging]")
{
auto check_angle = [](const ExPolygons &lower, const ExPolygon &bridge, double expected, double tolerance = -1, double expected_coverage = -1)
{
if (expected_coverage < 0)
expected_coverage = bridge.area();
BridgeDetector bridge_detector(bridge, lower, scaled<coord_t>(0.5)); // extrusion width
if (tolerance < 0)
tolerance = Geometry::rad2deg(bridge_detector.resolution) + EPSILON;
bridge_detector.detect_angle();
double result = bridge_detector.angle;
Polygons coverage = bridge_detector.coverage();
THEN("correct coverage area") {
REQUIRE(is_approx(area(coverage), expected_coverage));
}
// our epsilon is equal to the steps used by the bridge detection algorithm
//##use XXX; YYY [ rad2deg($result), $expected ];
// returned value must be non-negative, check for that too
double delta = Geometry::rad2deg(result) - expected;
if (delta >= 180. - EPSILON)
delta -= 180;
return result >= 0. && std::abs(delta) < tolerance;
};
GIVEN("O-shaped overhang") {
auto test = [&check_angle](const Point &size, double rotate, double expected_angle, double tolerance = -1) {
ExPolygon lower{
Polygon::new_scale({ {-2,-2}, {size.x()+2,-2}, {size.x()+2,size.y()+2}, {-2,size.y()+2} }),
Polygon::new_scale({ {0,0}, {0,size.y()}, {size.x(),size.y()}, {size.x(),0} } )
};
lower.rotate(Geometry::deg2rad(rotate), size / 2);
ExPolygon bridge_expoly(lower.holes.front());
bridge_expoly.contour.reverse();
return check_angle({ lower }, bridge_expoly, expected_angle, tolerance);
};
WHEN("Bridge size 20x10") {
bool valid = test({20,10}, 0., 90.);
THEN("bridging angle is 90 degrees") {
REQUIRE(valid);
}
}
WHEN("Bridge size 10x20") {
bool valid = test({10,20}, 0., 0.);
THEN("bridging angle is 0 degrees") {
REQUIRE(valid);
}
}
WHEN("Bridge size 20x10, rotated by 45 degrees") {
bool valid = test({20,10}, 45., 135., 20.);
THEN("bridging angle is 135 degrees") {
REQUIRE(valid);
}
}
WHEN("Bridge size 20x10, rotated by 135 degrees") {
bool valid = test({20,10}, 135., 45., 20.);
THEN("bridging angle is 45 degrees") {
REQUIRE(valid);
}
}
}
GIVEN("two-sided bridge") {
ExPolygon bridge{ Polygon::new_scale({ {0,0}, {20,0}, {20,10}, {0,10} }) };
ExPolygons lower { ExPolygon{ Polygon::new_scale({ {-2,0}, {0,0}, {0,10}, {-2,10} }) } };
lower.emplace_back(lower.front());
lower.back().translate(Point::new_scale(22, 0));
THEN("Bridging angle 0 degrees") {
REQUIRE(check_angle(lower, bridge, 0));
}
}
GIVEN("for C-shaped overhang") {
ExPolygon bridge{ Polygon::new_scale({ {0,0}, {20,0}, {10,10}, {0,10} }) };
ExPolygon lower{ Polygon::new_scale({ {0,0}, {0,10}, {10,10}, {10,12}, {-2,12}, {-2,-2}, {22,-2}, {22,0} }) };
bool valid = check_angle({ lower }, bridge, 135);
THEN("Bridging angle is 135 degrees") {
REQUIRE(valid);
}
}
GIVEN("square overhang with L-shaped anchors") {
ExPolygon bridge{ Polygon::new_scale({ {10,10}, {20,10}, {20,20}, {10,20} }) };
ExPolygon lower{ Polygon::new_scale({ {10,10}, {10,20}, {20,20}, {30,30}, {0,30}, {0,0} }) };
bool valid = check_angle({ lower }, bridge, 45., -1., bridge.area() / 2.);
THEN("Bridging angle is 45 degrees") {
REQUIRE(valid);
}
}
}
SCENARIO("Bridging integration", "[Bridging]") {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config_with({
{ "top_solid_layers", 0 },
// to prevent bridging on sparse infill
{ "bridge_speed", 99 }
});
std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::bridge }, config);
GCodeReader parser;
const double bridge_speed = config.opt_float("bridge_speed") * 60.;
// angle => length
std::map<coord_t, double> extrusions;
parser.parse_buffer(gcode, [&extrusions, bridge_speed](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
{
// if the command is a T command, set the the current tool
if (line.cmd() == "G1" && is_approx<double>(bridge_speed, line.new_F(self))) {
// Accumulate lengths of bridging extrusions according to bridging angle.
Line l{ self.xy_scaled(), line.new_XY_scaled(self) };
size_t angle = scaled<coord_t>(l.direction());
auto it = extrusions.find(angle);
if (it == extrusions.end())
it = extrusions.insert(std::make_pair(angle, 0.)).first;
it->second += l.length();
}
});
THEN("bridge is generated") {
REQUIRE(! extrusions.empty());
}
THEN("bridge has the expected direction 0 degrees") {
// Bridging with the longest extrusion.
auto it_longest_extrusion = std::max_element(extrusions.begin(), extrusions.end(),
[](const auto &e1, const auto &e2){ return e1.second < e2.second; });
REQUIRE(it_longest_extrusion->first == 0);
}
}

View File

@ -197,8 +197,7 @@ TEST_CASE("Fill: Pattern Path Length", "[Fill]") {
SCENARIO("Infill does not exceed perimeters", "[Fill]")
{
auto test = [](const std::string_view pattern) {
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
config.set_deserialize_strict({
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config_with({
{ "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" },
{ "fill_pattern", pattern },
{ "top_fill_pattern", pattern },

View File

@ -294,7 +294,7 @@ SCENARIO("Various Clipper operations - t/clipper.t", "[ClipperUtils]") {
WHEN("clipping a line") {
auto line = Polyline::new_scale({ { 152.742,288.086671142818 }, { 152.742,34.166466971035 } });
Polylines intersection = intersection_pl({ line }, { circle_with_hole });
Polylines intersection = intersection_pl(line, Polygons{ circle_with_hole });
THEN("clipped to two pieces") {
REQUIRE(intersection.front().length() == Approx((Vec2d(152742000, 215178843) - Vec2d(152742000, 288086661)).norm()));
REQUIRE(intersection[1].length() == Approx((Vec2d(152742000, 35166477) - Vec2d(152742000, 108087507)).norm()));

View File

@ -44,10 +44,8 @@ set(XSP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/xsp)
#FIXME list the dependecies explicitely, add dependency on the typemap.
set(XS_XSP_FILES
${XSP_DIR}/BoundingBox.xsp
${XSP_DIR}/BridgeDetector.xsp
${XSP_DIR}/Config.xsp
${XSP_DIR}/ExPolygon.xsp
${XSP_DIR}/ExPolygonCollection.xsp
${XSP_DIR}/ExtrusionEntityCollection.xsp
${XSP_DIR}/ExtrusionLoop.xsp
${XSP_DIR}/ExtrusionMultiPath.xsp

View File

@ -48,11 +48,6 @@ use overload
'@{}' => sub { $_[0]->arrayref },
'fallback' => 1;
package Slic3r::ExPolygon::Collection;
use overload
'@{}' => sub { $_[0]->arrayref },
'fallback' => 1;
package Slic3r::ExtrusionPath::Collection;
use overload
'@{}' => sub { $_[0]->arrayref },
@ -179,7 +174,6 @@ sub new {
package main;
for my $class (qw(
Slic3r::BridgeDetector
Slic3r::Config
Slic3r::Config::Full
Slic3r::Config::GCode
@ -188,7 +182,6 @@ for my $class (qw(
Slic3r::Config::PrintRegion
Slic3r::Config::Static
Slic3r::ExPolygon
Slic3r::ExPolygon::Collection
Slic3r::ExtrusionLoop
Slic3r::ExtrusionMultiPath
Slic3r::ExtrusionPath

View File

@ -4,7 +4,6 @@
namespace Slic3r {
REGISTER_CLASS(ExPolygon, "ExPolygon");
REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection");
REGISTER_CLASS(ExtrusionMultiPath, "ExtrusionMultiPath");
REGISTER_CLASS(ExtrusionPath, "ExtrusionPath");
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
@ -29,7 +28,6 @@ REGISTER_CLASS(ModelInstance, "Model::Instance");
REGISTER_CLASS(BoundingBox, "Geometry::BoundingBox");
REGISTER_CLASS(BoundingBoxf, "Geometry::BoundingBoxf");
REGISTER_CLASS(BoundingBoxf3, "Geometry::BoundingBoxf3");
REGISTER_CLASS(BridgeDetector, "BridgeDetector");
REGISTER_CLASS(Point, "Point");
__REGISTER_CLASS(Vec2d, "Pointf");
__REGISTER_CLASS(Vec3d, "Pointf3");

View File

@ -5,7 +5,7 @@ use warnings;
use List::Util qw(first sum);
use Slic3r::XS;
use Test::More tests => 21;
use Test::More tests => 15;
use constant PI => 4 * atan2(1, 1);
@ -81,28 +81,4 @@ is $expolygon->area, 100*100-20*20, 'area';
], 'rotate around pure-Perl Point';
}
{
my $expolygon2 = $expolygon->clone;
$expolygon2->scale(2);
my $collection = Slic3r::ExPolygon::Collection->new($expolygon->pp, $expolygon2->pp);
is_deeply $collection->pp, [ $expolygon->pp, $expolygon2->pp ],
'expolygon collection (pure Perl) roundtrip';
my $collection2 = Slic3r::ExPolygon::Collection->new($expolygon, $expolygon2);
is_deeply $collection->pp, $collection2->pp,
'expolygon collection (XS) roundtrip';
$collection->clear;
is scalar(@$collection), 0, 'clear collection';
$collection->append($expolygon);
is scalar(@$collection), 1, 'append to collection';
my $exp = $collection->[0];
$exp->scale(3);
is $collection->[0][0][0][0], $exp->[0][0][0], 'collection items are returned by reference';
is_deeply $collection->[0]->clone->pp, $collection->[0]->pp, 'clone collection item';
}
__END__

View File

@ -1,43 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/BridgeDetector.hpp"
%}
%name{Slic3r::BridgeDetector} class BridgeDetector {
~BridgeDetector();
bool detect_angle();
Polygons coverage();
%name{coverage_by_angle} Polygons coverage(double angle);
Polylines unsupported_edges();
%name{unsupported_edges_by_angle} Polylines unsupported_edges(double angle);
double angle()
%code{% RETVAL = THIS->angle; %};
double resolution()
%code{% RETVAL = THIS->resolution; %};
%{
BridgeDetector*
BridgeDetector::new(expolygon, lower_slices, extrusion_width)
ExPolygon* expolygon;
ExPolygonCollection* lower_slices;
int extrusion_width;
CODE:
RETVAL = new BridgeDetector(*expolygon, lower_slices->expolygons, extrusion_width);
OUTPUT:
RETVAL
BridgeDetector*
BridgeDetector::new_expolygons(expolygons, lower_slices, extrusion_width)
ExPolygonCollection* expolygons;
ExPolygonCollection* lower_slices;
int extrusion_width;
CODE:
RETVAL = new BridgeDetector(expolygons->expolygons, lower_slices->expolygons, extrusion_width);
OUTPUT:
RETVAL
%}
};

View File

@ -1,81 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/ExPolygonCollection.hpp"
%}
%name{Slic3r::ExPolygon::Collection} class ExPolygonCollection {
~ExPolygonCollection();
Clone<ExPolygonCollection> clone()
%code{% RETVAL = THIS; %};
void clear()
%code{% THIS->expolygons.clear(); %};
void scale(double factor);
void translate(double x, double y);
void rotate(double angle, Point* center)
%code{% THIS->rotate(angle, *center); %};
int count()
%code{% RETVAL = THIS->expolygons.size(); %};
bool contains_point(Point* point)
%code{% RETVAL = THIS->contains(*point); %};
bool contains_line(Line* line)
%code{% RETVAL = THIS->contains(*line); %};
bool contains_polyline(Polyline* polyline)
%code{% RETVAL = THIS->contains(*polyline); %};
void simplify(double tolerance);
Polygons polygons()
%code{% RETVAL = (Polygons)*THIS; %};
Clone<Polygon> convex_hull();
%{
ExPolygonCollection*
ExPolygonCollection::new(...)
CODE:
RETVAL = new ExPolygonCollection ();
// ST(0) is class name, others are expolygons
RETVAL->expolygons.resize(items-1);
for (unsigned int i = 1; i < items; i++) {
// Note: a COPY of the input is stored
from_SV_check(ST(i), &RETVAL->expolygons[i-1]);
}
OUTPUT:
RETVAL
SV*
ExPolygonCollection::arrayref()
CODE:
AV* av = newAV();
av_fill(av, THIS->expolygons.size()-1);
int i = 0;
for (ExPolygons::iterator it = THIS->expolygons.begin(); it != THIS->expolygons.end(); ++it) {
av_store(av, i++, perl_to_SV_ref(*it));
}
RETVAL = newRV_noinc((SV*)av);
OUTPUT:
RETVAL
SV*
ExPolygonCollection::pp()
CODE:
AV* av = newAV();
av_fill(av, THIS->expolygons.size()-1);
int i = 0;
for (ExPolygons::iterator it = THIS->expolygons.begin(); it != THIS->expolygons.end(); ++it) {
av_store(av, i++, to_SV_pureperl(&*it));
}
RETVAL = newRV_noinc((SV*)av);
OUTPUT:
RETVAL
void
ExPolygonCollection::append(...)
CODE:
for (unsigned int i = 1; i < items; i++) {
ExPolygon expolygon;
from_SV_check(ST(i), &expolygon);
THIS->expolygons.push_back(expolygon);
}
%}
};

View File

@ -95,22 +95,6 @@ ExtrusionPath::append(...)
THIS->polyline.points.push_back(p);
}
ExtrusionEntityCollection*
ExtrusionPath::intersect_expolygons(ExPolygonCollection* collection)
CODE:
RETVAL = new ExtrusionEntityCollection ();
THIS->intersect_expolygons(*collection, RETVAL);
OUTPUT:
RETVAL
ExtrusionEntityCollection*
ExtrusionPath::subtract_expolygons(ExPolygonCollection* collection)
CODE:
RETVAL = new ExtrusionEntityCollection ();
THIS->subtract_expolygons(*collection, RETVAL);
OUTPUT:
RETVAL
%}
};

View File

@ -64,14 +64,6 @@ rad2deg(angle)
OUTPUT:
RETVAL
double
rad2deg_dir(angle)
double angle
CODE:
RETVAL = Slic3r::Geometry::rad2deg_dir(angle);
OUTPUT:
RETVAL
double
deg2rad(angle)
double angle

View File

@ -3,7 +3,6 @@
%{
#include <xsinit.h>
#include "libslic3r/Layer.hpp"
#include "libslic3r/ExPolygonCollection.hpp"
%}
%name{Slic3r::Layer::Region} class LayerRegion {
@ -59,9 +58,6 @@
Ref<LayerRegion> get_region(int idx);
Ref<LayerRegion> add_region(PrintRegion* print_region);
ExPolygonCollection* slices()
%code%{ RETVAL = new ExPolygonCollection(THIS->lslices); %};
int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %};

View File

@ -88,10 +88,6 @@ ExPolygon* O_OBJECT_SLIC3R
Ref<ExPolygon> O_OBJECT_SLIC3R_T
Clone<ExPolygon> O_OBJECT_SLIC3R_T
ExPolygonCollection* O_OBJECT_SLIC3R
Ref<ExPolygonCollection> O_OBJECT_SLIC3R_T
Clone<ExPolygonCollection> O_OBJECT_SLIC3R_T
ExtrusionEntityCollection* O_OBJECT_SLIC3R
Ref<ExtrusionEntityCollection> O_OBJECT_SLIC3R_T
Clone<ExtrusionEntityCollection> O_OBJECT_SLIC3R_T
@ -163,10 +159,6 @@ GCode* O_OBJECT_SLIC3R
Ref<GCode> O_OBJECT_SLIC3R_T
Clone<GCode> O_OBJECT_SLIC3R_T
BridgeDetector* O_OBJECT_SLIC3R
Ref<BridgeDetector> O_OBJECT_SLIC3R_T
Clone<BridgeDetector> O_OBJECT_SLIC3R_T
Axis T_UV
ExtrusionLoopRole T_UV
ExtrusionRole T_UV

View File

@ -54,9 +54,6 @@
%typemap{ExPolygon*};
%typemap{Ref<ExPolygon>}{simple};
%typemap{Clone<ExPolygon>}{simple};
%typemap{ExPolygonCollection*};
%typemap{Ref<ExPolygonCollection>}{simple};
%typemap{Clone<ExPolygonCollection>}{simple};
%typemap{Flow*};
%typemap{Ref<Flow>}{simple};
%typemap{Clone<Flow>}{simple};
@ -87,9 +84,6 @@
%typemap{TriangleMesh*};
%typemap{Ref<TriangleMesh>}{simple};
%typemap{Clone<TriangleMesh>}{simple};
%typemap{BridgeDetector*};
%typemap{Ref<BridgeDetector>}{simple};
%typemap{Clone<BridgeDetector>}{simple};
%typemap{SurfaceCollection*};
%typemap{Ref<SurfaceCollection>}{simple};
%typemap{Clone<SurfaceCollection>}{simple};