Port offset_ex() and offset2_ex() to XS

xsdata-boost
Alessandro Ranellucci 2013-07-16 20:09:53 +02:00
parent 9458c7db97
commit 898007fc36
20 changed files with 4349 additions and 52 deletions

View File

@ -51,12 +51,12 @@ sub offset {
sub offset_ex {
my $self = shift;
return Slic3r::Geometry::Clipper::offset_ex($self, @_);
return Slic3r::Geometry::Clipper::offset_ex(\@$self, @_);
}
sub safety_offset {
my $self = shift;
return Slic3r::Geometry::Clipper::safety_offset_ex($self, @_);
return Slic3r::Geometry::Clipper::safety_offset_ex(\@$self, @_);
}
sub noncollapsing_offset_ex {
@ -69,7 +69,7 @@ sub noncollapsing_offset_ex {
sub encloses_point {
my $self = shift;
my ($point) = @_;
return Boost::Geometry::Utils::point_covered_by_polygon($point->arrayref, $self->pp);
return Boost::Geometry::Utils::point_covered_by_polygon($point->pp, $self->pp);
}
# A version of encloses_point for use when hole borders do not matter.
@ -78,7 +78,7 @@ sub encloses_point {
sub encloses_point_quick {
my $self = shift;
my ($point) = @_;
return Boost::Geometry::Utils::point_within_polygon($point->arrayref, $self->arrayref);
return Boost::Geometry::Utils::point_within_polygon($point->pp, $self->pp);
}
sub encloses_line {

View File

@ -115,7 +115,7 @@ sub fill_surface {
# clip paths again to prevent connection segments from crossing the expolygon boundaries
@paths = map Slic3r::Polyline->new(@$_),
@{ Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection(
[ map $_->pp, $surface->expolygon->offset_ex(scaled_epsilon) ],
[ map $_->pp, @{$surface->expolygon->offset_ex(scaled_epsilon)} ],
[ map $_->pp, @paths ],
) } if @paths; # this temporary check is a workaround for the multilinestring bug in B::G::U
}

View File

@ -16,7 +16,7 @@ sub fill_surface {
my $rotate_vector = $self->infill_direction($surface);
$self->rotate_points($expolygon, $rotate_vector);
my ($expolygon_off) = $expolygon->offset_ex(scale $params{flow_spacing}/2);
my $expolygon_off = $expolygon->offset_ex(scale $params{flow_spacing}/2)->[0];
return {} if !defined $expolygon_off; # skip some very small polygons (which shouldn't arrive here)
my $flow_spacing = $params{flow_spacing};
@ -67,7 +67,7 @@ sub fill_surface {
# are kept even if the expolygon has vertical sides
my @polylines = map Slic3r::Polyline->new(@$_),
@{ Boost::Geometry::Utils::multi_polygon_multi_linestring_intersection(
[ map $_->pp, $expolygon->offset_ex(scaled_epsilon) ],
[ map $_->pp, @{ $expolygon->offset_ex(scaled_epsilon) } ],
[ map $_->pp, @{ $self->cache->{$cache_id} } ],
) };

View File

@ -49,7 +49,7 @@ sub BUILD {
# so that no motion along external perimeters happens
$self->_inner->[$i] = $self->no_internal
? []
: [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ];
: $self->islands->[$i]->offset_ex(-$self->_inner_margin);
# offset the island outwards to make the boundaries for external movements
$self->_outer->[$i] = [ offset([ $self->islands->[$i]->contour], $self->_outer_margin) ];

View File

@ -45,26 +45,6 @@ sub offset2 {
return @$offsets;
}
sub offset_ex {
my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_;
$scale ||= 100000;
$joinType //= JT_MITER;
$miterLimit //= 3;
my $offsets = Math::Clipper::ex_int_offset(_convert($polygons), $distance, $scale, $joinType, $miterLimit);
return map Slic3r::ExPolygon->new($_->{outer}, @{$_->{holes}}), @$offsets;
}
sub offset2_ex {
my ($polygons, $delta1, $delta2, $scale, $joinType, $miterLimit) = @_;
$scale ||= 100000;
$joinType //= JT_MITER;
$miterLimit //= 3;
my $offsets = Math::Clipper::ex_int_offset2(_convert($polygons), $delta1, $delta2, $scale, $joinType, $miterLimit);
return map Slic3r::ExPolygon->new($_->{outer}, @{$_->{holes}}), @$offsets;
}
sub diff_ex {
my ($subject, $clip, $safety_offset) = @_;
@ -147,7 +127,7 @@ sub xor_ex {
sub collapse_ex {
my ($polygons, $width) = @_;
return [ offset2_ex($polygons, -$width/2, +$width/2) ];
return offset2_ex($polygons, -$width/2, +$width/2);
}
sub simplify_polygon {

View File

@ -5,7 +5,7 @@ use List::Util qw(sum first);
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Geometry qw(PI A B scale chained_path_items points_coincide);
use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex
offset offset2 offset2_ex PFT_EVENODD union_pt traverse_pt diff intersection);
offset offset2 offset_ex offset2_ex PFT_EVENODD union_pt traverse_pt diff intersection);
use Slic3r::Surface ':types';
has 'layer' => (
@ -141,7 +141,7 @@ sub _merge_loops {
$expolygons = diff_ex([ map @$_, @$expolygons ], [$loop]);
}
}
$expolygons = [ map $_->offset_ex(-$safety_offset), @$expolygons ];
$expolygons = offset_ex([ map @$_, @$expolygons ], -$safety_offset);
Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n",
scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops);
@ -180,7 +180,7 @@ sub make_perimeters {
? $perimeter_spacing / 2
: $perimeter_spacing;
my @offsets = offset2_ex(\@last, -1.5*$spacing, +0.5*$spacing);
my @offsets = @{offset2_ex(\@last, -1.5*$spacing, +0.5*$spacing)};
my @contours_offsets = map $_->contour, @offsets;
my @holes_offsets = map $_->holes, @offsets;
@offsets = (@contours_offsets, @holes_offsets); # turn @offsets from ExPolygons to Polygons
@ -209,11 +209,11 @@ sub make_perimeters {
# non-collapsing regions
# use a bogus surface_type
$self->fill_surfaces->append(
map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_TOP), offset2_ex(
map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_TOP), @{offset2_ex(
[ map $_->simplify(&Slic3r::SCALED_RESOLUTION), @last ],
-($perimeter_spacing/2 + $infill_spacing),
+$infill_spacing,
)
)}
);
}
@ -322,8 +322,8 @@ sub _fill_gaps {
my $flow = $self->perimeter_flow->clone(width => $width);
# extract the gaps having this width
my @this_width = map $_->offset_ex(+0.5*$flow->scaled_width),
map $_->noncollapsing_offset_ex(-0.5*$flow->scaled_width),
my @this_width = map @{$_->offset_ex(+0.5*$flow->scaled_width)},
map @{$_->noncollapsing_offset_ex(-0.5*$flow->scaled_width)},
@$gaps;
if (0) { # remember to re-enable t/dynamic.t
@ -346,7 +346,7 @@ sub _fill_gaps {
# fill gaps using zigzag infill
# since this is infill, we have to offset by half-extrusion width inwards
my @infill = map $_->offset_ex(-0.5*$flow->scaled_width), @this_width;
my @infill = map @{$_->offset_ex(-0.5*$flow->scaled_width)}, @this_width;
foreach my $expolygon (@infill) {
my @paths = $filler->fill_surface(
@ -520,7 +520,7 @@ sub _detect_bridges {
}
} elsif (@edges) {
# inset the bridge expolygon; we'll use this one to clip our test lines
my $inset = [ $surface->expolygon->offset_ex($self->infill_flow->scaled_width) ];
my $inset = $surface->expolygon->offset_ex($self->infill_flow->scaled_width);
# detect anchors as intersection between our bridge expolygon and the lower slices
my $anchors = intersection_ex(

View File

@ -766,9 +766,9 @@ sub write_gcode {
]);
# discard layers only containing thin walls (offset would fail on an empty polygon)
if (@$convex_hull) {
my @island = Slic3r::ExPolygon->new($convex_hull)
->translate(scale $shift[X], scale $shift[Y])
->offset_ex(scale $distance_from_objects, 1, JT_SQUARE);
my $expolygon = Slic3r::ExPolygon->new($convex_hull);
$expolygon->translate(scale $shift[X], scale $shift[Y]);
my @island = @{$expolygon->offset_ex(scale $distance_from_objects, 1, JT_SQUARE)};
foreach my $copy (@{ $self->objects->[$obj_idx]->copies }) {
push @islands, map $_->clone->translate(@$copy), @island;
}

View File

@ -488,9 +488,9 @@ sub clip_fill_surfaces {
my $overhang_width = $layerm->overhang_width;
# we want to support any solid surface, not just tops
# (internal solids might have been generated)
push @overhangs, map $_->offset_ex($additional_margin), @{intersection_ex(
push @overhangs, map @{$_->offset_ex($additional_margin)}, @{intersection_ex(
[ map @{$_->expolygon}, grep $_->surface_type != S_TYPE_INTERNAL, @{$layerm->fill_surfaces} ],
[ map @$_, map $_->offset_ex(-$overhang_width), @{$lower_layer->slices} ],
[ map @$_, map @{$_->offset_ex(-$overhang_width)}, @{$lower_layer->slices} ],
)};
}
}
@ -825,7 +825,7 @@ sub generate_support_material {
my $layer = $self->layers->[$i];
my $lower_layer = $i > 0 ? $self->layers->[$i-1] : undef;
my @current_layer_offsetted_slices = map $_->offset_ex($distance_from_object), @{$layer->slices};
my @current_layer_offsetted_slices = map @{$_->offset_ex($distance_from_object)}, @{$layer->slices};
# $upper_layers_overhangs[-1] contains the overhangs of the upper layer, regardless of any interface layers
# $upper_layers_overhangs[0] contains the overhangs of the first upper layer above the interface layers
@ -884,7 +884,7 @@ sub generate_support_material {
? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad))
: $self->layers->[1]->regions->[0]->overhang_width;
@overhangs = map $_->offset_ex(+$distance), @{diff_ex(
@overhangs = map @{$_->offset_ex(+$distance)}, @{diff_ex(
[ map @$_, @{$layer->slices} ],
[ map @$_, @{$lower_layer->slices} ],
1,
@ -906,7 +906,7 @@ sub generate_support_material {
my $support_interface_patterns = [];
{
# 0.5 ensures the paths don't get clipped externally when applying them to layers
my @areas = map $_->offset_ex(- 0.5 * $flow->scaled_width),
my @areas = map @{$_->offset_ex(- 0.5 * $flow->scaled_width)},
@{union_ex([ map $_->contour, map @$_, values %layers, values %layers_interfaces, values %layers_contact_areas ])};
my $pattern = $Slic3r::Config->support_material_pattern;

View File

@ -35,7 +35,7 @@ sub group {
sub offset {
my $self = shift;
return map $self->clone(expolygon => $_), $self->expolygon->offset_ex(@_);
return map $self->clone(expolygon => $_), @{$self->expolygon->offset_ex(@_)};
}
sub simplify {

View File

@ -22,7 +22,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
scale_points [0,0], [10,0], [10,10], [0,10],
]);
my @offsets = $square->noncollapsing_offset_ex(- scale 5);
my @offsets = @{$square->noncollapsing_offset_ex(- scale 5)};
is scalar @offsets, 1, 'non-collapsing offset';
}

102
xs/src/ClipperUtils.hpp Normal file
View File

@ -0,0 +1,102 @@
#ifndef slic3r_ClipperUtils_hpp_
#define slic3r_ClipperUtils_hpp_
extern "C" {
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
}
#include "clipper.hpp"
namespace Slic3r {
void
ClipperPolygon_to_Slic3rPolygon(ClipperLib::Polygon &input, Slic3r::Polygon &output)
{
output.points.clear();
for (ClipperLib::Polygon::iterator pit = input.begin(); pit != input.end(); ++pit) {
output.points.push_back(Slic3r::Point( (*pit).X, (*pit).Y ));
}
}
//-----------------------------------------------------------
// legacy code from Clipper documentation
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons)
{
size_t cnt = expolygons.size();
expolygons.resize(cnt + 1);
ClipperPolygon_to_Slic3rPolygon(polynode.Contour, expolygons[cnt].contour);
expolygons[cnt].holes.resize(polynode.ChildCount());
for (int i = 0; i < polynode.ChildCount(); ++i)
{
ClipperPolygon_to_Slic3rPolygon(polynode.Childs[i]->Contour, expolygons[cnt].holes[i]);
//Add outer polygons contained by (nested within) holes ...
for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j)
AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons);
}
}
void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons)
{
expolygons.clear();
for (int i = 0; i < polytree.ChildCount(); ++i)
AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons);
}
//-----------------------------------------------------------
void
Slic3rPolygon_to_ClipperPolygon(Slic3r::Polygon &input, ClipperLib::Polygon &output)
{
output.clear();
for (Slic3r::Points::iterator pit = input.points.begin(); pit != input.points.end(); ++pit) {
output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
}
}
void
Slic3rPolygons_to_ClipperPolygons(Slic3r::Polygons &input, ClipperLib::Polygons &output)
{
output.clear();
for (Slic3r::Polygons::iterator it = input.begin(); it != input.end(); ++it) {
ClipperLib::Polygon p;
Slic3rPolygon_to_ClipperPolygon(*it, p);
output.push_back(p);
}
}
void
ClipperPolygons_to_Slic3rExPolygons(ClipperLib::Polygons &input, Slic3r::ExPolygons &output)
{
// init Clipper
ClipperLib::Clipper clipper;
clipper.Clear();
// perform union
clipper.AddPolygons(input, ClipperLib::ptSubject);
ClipperLib::PolyTree* polytree = new ClipperLib::PolyTree();
clipper.Execute(ClipperLib::ctUnion, *polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // offset results work with both EvenOdd and NonZero
// write to ExPolygons object
output.clear();
PolyTreeToExPolygons(*polytree, output);
delete polytree;
}
void
scaleClipperPolygons(ClipperLib::Polygons &polygons, const double scale)
{
for (ClipperLib::Polygons::iterator it = polygons.begin(); it != polygons.end(); ++it) {
for (ClipperLib::Polygon::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) {
(*pit).X *= scale;
(*pit).Y *= scale;
}
}
}
}
#endif

View File

@ -22,6 +22,7 @@ class ExPolygon
void from_SV(SV* poly_sv);
void from_SV_check(SV* poly_sv);
SV* to_SV();
SV* to_SV_ref();
SV* to_SV_pureperl();
void scale(double factor);
void translate(double x, double y);
@ -75,6 +76,13 @@ ExPolygon::to_SV() {
return newRV_noinc((SV*)av);
}
SV*
ExPolygon::to_SV_ref() {
SV* sv = newSV(0);
sv_setref_pv( sv, "Slic3r::ExPolygon", new ExPolygon(*this) );
return sv;
}
SV*
ExPolygon::to_SV_pureperl()
{

View File

@ -15,6 +15,7 @@ namespace Slic3r {
class Polygon : public MultiPoint {
public:
SV* to_SV_ref();
Lines lines();
Polyline* split_at_index(int index);
Polyline* split_at_first_point();
@ -22,6 +23,13 @@ class Polygon : public MultiPoint {
typedef std::vector<Polygon> Polygons;
SV*
Polygon::to_SV_ref() {
SV* sv = newSV(0);
sv_setref_pv( sv, "Slic3r::Polygon", new Polygon(*this) );
return sv;
}
Lines
Polygon::lines()
{

3700
xs/src/clipper.cpp Executable file

File diff suppressed because it is too large Load Diff

349
xs/src/clipper.hpp Executable file
View File

@ -0,0 +1,349 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 5.1.6 *
* Date : 23 May 2013 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2013 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt *
* *
* Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
* "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 *
* *
* Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston *
* *
* See also: *
* "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
#ifndef clipper_hpp
#define clipper_hpp
#include <vector>
#include <stdexcept>
#include <cstring>
#include <cstdlib>
#include <ostream>
namespace ClipperLib {
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip };
//By far the most widely used winding rules for polygon filling are
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
//see http://glprogramming.com/red/chapter11.html
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
typedef signed long long long64;
typedef unsigned long long ulong64;
struct IntPoint {
public:
long64 X;
long64 Y;
IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {};
friend std::ostream& operator <<(std::ostream &s, IntPoint &p);
};
typedef std::vector< IntPoint > Polygon;
typedef std::vector< Polygon > Polygons;
std::ostream& operator <<(std::ostream &s, Polygon &p);
std::ostream& operator <<(std::ostream &s, Polygons &p);
class PolyNode;
typedef std::vector< PolyNode* > PolyNodes;
class PolyNode
{
public:
PolyNode();
Polygon Contour;
PolyNodes Childs;
PolyNode* Parent;
PolyNode* GetNext() const;
bool IsHole() const;
int ChildCount() const;
private:
PolyNode* GetNextSiblingUp() const;
unsigned Index; //node index in Parent.Childs
void AddChild(PolyNode& child);
friend class Clipper; //to access Index
};
class PolyTree: public PolyNode
{
public:
~PolyTree(){Clear();};
PolyNode* GetFirst() const;
void Clear();
int Total() const;
private:
PolyNodes AllNodes;
friend class Clipper; //to access AllNodes
};
enum JoinType { jtSquare, jtRound, jtMiter };
enum EndType { etClosed, etButt, etSquare, etRound};
bool Orientation(const Polygon &poly);
double Area(const Polygon &poly);
void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys,
double delta, JoinType jointype = jtSquare, double limit = 0, bool autoFix = true);
void OffsetPolyLines(const Polygons &in_lines, Polygons &out_lines,
double delta, JoinType jointype = jtSquare, EndType endtype = etSquare, double limit = 0, bool autoFix = true);
void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
void SimplifyPolygons(Polygons &polys, PolyFillType fillType = pftEvenOdd);
void CleanPolygon(const Polygon& in_poly, Polygon& out_poly, double distance = 1.415);
void CleanPolygons(const Polygons& in_polys, Polygons& out_polys, double distance = 1.415);
void PolyTreeToPolygons(const PolyTree& polytree, Polygons& polygons);
void ReversePolygon(Polygon& p);
void ReversePolygons(Polygons& p);
//used internally ...
enum EdgeSide { esLeft = 1, esRight = 2};
enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 };
//inline IntersectProtects operator|(IntersectProtects a, IntersectProtects b)
//{return static_cast<IntersectProtects>(static_cast<int>(a) | static_cast<int>(b));}
struct TEdge {
long64 xbot;
long64 ybot;
long64 xcurr;
long64 ycurr;
long64 xtop;
long64 ytop;
double dx;
long64 deltaX;
long64 deltaY;
PolyType polyType;
EdgeSide side;
int windDelta; //1 or -1 depending on winding direction
int windCnt;
int windCnt2; //winding count of the opposite polytype
int outIdx;
TEdge *next;
TEdge *prev;
TEdge *nextInLML;
TEdge *nextInAEL;
TEdge *prevInAEL;
TEdge *nextInSEL;
TEdge *prevInSEL;
};
struct IntersectNode {
TEdge *edge1;
TEdge *edge2;
IntPoint pt;
IntersectNode *next;
};
struct LocalMinima {
long64 Y;
TEdge *leftBound;
TEdge *rightBound;
LocalMinima *next;
};
struct Scanbeam {
long64 Y;
Scanbeam *next;
};
struct OutPt; //forward declaration
struct OutRec {
int idx;
bool isHole;
OutRec *FirstLeft; //see comments in clipper.pas
PolyNode *polyNode;
OutPt *pts;
OutPt *bottomPt;
};
struct OutPt {
int idx;
IntPoint pt;
OutPt *next;
OutPt *prev;
};
struct JoinRec {
IntPoint pt1a;
IntPoint pt1b;
int poly1Idx;
IntPoint pt2a;
IntPoint pt2b;
int poly2Idx;
};
struct HorzJoinRec {
TEdge *edge;
int savedIdx;
};
struct IntRect { long64 left; long64 top; long64 right; long64 bottom; };
typedef std::vector < OutRec* > PolyOutList;
typedef std::vector < TEdge* > EdgeList;
typedef std::vector < JoinRec* > JoinList;
typedef std::vector < HorzJoinRec* > HorzJoinList;
//ClipperBase is the ancestor to the Clipper class. It should not be
//instantiated directly. This class simply abstracts the conversion of sets of
//polygon coordinates into edge objects that are stored in a LocalMinima list.
class ClipperBase
{
public:
ClipperBase();
virtual ~ClipperBase();
bool AddPolygon(const Polygon &pg, PolyType polyType);
bool AddPolygons( const Polygons &ppg, PolyType polyType);
virtual void Clear();
IntRect GetBounds();
protected:
void DisposeLocalMinimaList();
TEdge* AddBoundsToLML(TEdge *e);
void PopLocalMinima();
virtual void Reset();
void InsertLocalMinima(LocalMinima *newLm);
LocalMinima *m_CurrentLM;
LocalMinima *m_MinimaList;
bool m_UseFullRange;
EdgeList m_edges;
};
class Clipper : public virtual ClipperBase
{
public:
Clipper();
~Clipper();
bool Execute(ClipType clipType,
Polygons &solution,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
bool Execute(ClipType clipType,
PolyTree &polytree,
PolyFillType subjFillType = pftEvenOdd,
PolyFillType clipFillType = pftEvenOdd);
void Clear();
bool ReverseSolution() {return m_ReverseOutput;};
void ReverseSolution(bool value) {m_ReverseOutput = value;};
bool ForceSimple() {return m_ForceSimple;};
void ForceSimple(bool value) {m_ForceSimple = value;};
protected:
void Reset();
virtual bool ExecuteInternal();
private:
PolyOutList m_PolyOuts;
JoinList m_Joins;
HorzJoinList m_HorizJoins;
ClipType m_ClipType;
Scanbeam *m_Scanbeam;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
IntersectNode *m_IntersectNodes;
bool m_ExecuteLocked;
PolyFillType m_ClipFillType;
PolyFillType m_SubjFillType;
bool m_ReverseOutput;
bool m_UsingPolyTree;
bool m_ForceSimple;
void DisposeScanbeamList();
void SetWindingCount(TEdge& edge);
bool IsEvenOddFillType(const TEdge& edge) const;
bool IsEvenOddAltFillType(const TEdge& edge) const;
void InsertScanbeam(const long64 Y);
long64 PopScanbeam();
void InsertLocalMinimaIntoAEL(const long64 botY);
void InsertEdgeIntoAEL(TEdge *edge);
void AddEdgeToSEL(TEdge *edge);
void CopyAELToSEL();
void DeleteFromSEL(TEdge *e);
void DeleteFromAEL(TEdge *e);
void UpdateEdgeIntoAEL(TEdge *&e);
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
bool IsContributing(const TEdge& edge) const;
bool IsTopHorz(const long64 XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e, long64 topY);
void ProcessHorizontals();
void ProcessHorizontal(TEdge *horzEdge);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2,
const IntPoint &pt, const IntersectProtects protects);
OutRec* CreateOutRec();
void AddOutPt(TEdge *e, const IntPoint &pt);
void DisposeAllPolyPts();
void DisposeOutRec(PolyOutList::size_type index);
bool ProcessIntersections(const long64 botY, const long64 topY);
void InsertIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt);
void BuildIntersectList(const long64 botY, const long64 topY);
void ProcessIntersectList();
void ProcessEdgesAtTopOfScanbeam(const long64 topY);
void BuildResult(Polygons& polys);
void BuildResult2(PolyTree& polytree);
void SetHoleState(TEdge *e, OutRec *outrec);
void DisposeIntersectNodes();
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
bool IsHole(TEdge *e);
void FixHoleLinkage(OutRec &outrec);
void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1);
void ClearJoins();
void AddHorzJoin(TEdge *e, int idx);
void ClearHorzJoins();
bool JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2);
void FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class clipperException : public std::exception
{
public:
clipperException(const char* description): m_descr(description) {}
virtual ~clipperException() throw() {}
virtual const char* what() const throw() {return m_descr.c_str();}
private:
std::string m_descr;
};
//------------------------------------------------------------------------------
} //ClipperLib namespace
#endif //clipper_hpp

53
xs/t/11_clipper.t Normal file
View File

@ -0,0 +1,53 @@
#!/usr/bin/perl
use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 2;
my $square = [ # ccw
[100, 100],
[200, 100],
[200, 200],
[100, 200],
];
my $hole_in_square = [ # cw
[140, 140],
[140, 160],
[160, 160],
[160, 140],
];
my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
{
my $result = @{Slic3r::Geometry::Clipper::offset_ex([ @$expolygon ], 5)};
is_deeply $result->[0]->pp, [ [
[205, 95],
[205, 205],
[95, 205],
[95, 95],
], [
[145, 145],
[145, 155],
[155, 155],
[155, 145],
] ], 'offset_ex';
}
{
my $result = @{Slic3r::Geometry::Clipper::offset2_ex([ @$expolygon ], 5, -2)};
is_deeply $result->[0]->pp, [ [
[203, 97],
[203, 203],
[97, 203],
[97, 97],
], [
[143, 143],
[143, 157],
[157, 157],
[157, 143],
] ], 'offset_ex';
}
__END__

77
xs/xsp/Clipper.xsp Normal file
View File

@ -0,0 +1,77 @@
%module{Slic3r::XS};
%{
#include <myinit.h>
#include "clipper.hpp"
#include "ClipperUtils.hpp"
%}
%package{Slic3r::Geometry::Clipper};
%{
ExPolygons
offset_ex(polygons, delta, scale = 100000, joinType = ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons
const float delta
double scale
ClipperLib::JoinType joinType
double miterLimit
CODE:
// read input
ClipperLib::Polygons* input = new ClipperLib::Polygons();
Slic3rPolygons_to_ClipperPolygons(polygons, *input);
// scale input
scaleClipperPolygons(*input, scale);
// perform offset
ClipperLib::Polygons* output = new ClipperLib::Polygons();
ClipperLib::OffsetPolygons(*input, *output, (delta*scale), joinType, miterLimit);
delete input;
// unscale output
scaleClipperPolygons(*output, 1/scale);
// convert into ExPolygons
ClipperPolygons_to_Slic3rExPolygons(*output, RETVAL);
delete output;
OUTPUT:
RETVAL
ExPolygons
offset2_ex(polygons, delta1, delta2, scale = 100000, joinType = ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons
const float delta1
const float delta2
double scale
ClipperLib::JoinType joinType
double miterLimit
CODE:
// read input
ClipperLib::Polygons* input = new ClipperLib::Polygons();
Slic3rPolygons_to_ClipperPolygons(polygons, *input);
// scale input
scaleClipperPolygons(*input, scale);
// perform first offset
ClipperLib::Polygons* output1 = new ClipperLib::Polygons();
ClipperLib::OffsetPolygons(*input, *output1, (delta1*scale), joinType, miterLimit);
delete input;
// perform second offset
ClipperLib::Polygons* output2 = new ClipperLib::Polygons();
ClipperLib::OffsetPolygons(*output1, *output2, (delta2*scale), joinType, miterLimit);
delete output1;
// unscale output
scaleClipperPolygons(*output2, 1/scale);
// convert into ExPolygons
ClipperPolygons_to_Slic3rExPolygons(*output2, RETVAL);
delete output2;
OUTPUT:
RETVAL
%}

View File

@ -31,7 +31,6 @@ Line::new(...)
RETVAL->b.from_SV_check( ST(2) );
OUTPUT:
RETVAL
}
void
Line::rotate(angle, center_sv)

View File

@ -13,18 +13,37 @@ SurfaceCollection* O_OBJECT
ExtrusionRole T_UV
SurfaceType T_UV
ClipperLib::JoinType T_UV
Lines T_LINES
Lines T_ARRAYREF
Polygons T_ARRAYREF
ExPolygons T_ARRAYREF
INPUT
T_ARRAYREF
if (SvROK($arg) && SvTYPE(SvRV($arg)) == SVt_PVAV) {
AV* av = (AV*)SvRV($arg);
const unsigned int len = av_len(av)+1;
$type* tmp = new $type(len);
for (unsigned int i = 0; i < len; i++) {
SV** elem = av_fetch(av, i, 0);
(*tmp)[i].from_SV_check(*elem);
}
$var = *tmp;
delete tmp;
} else
Perl_croak(aTHX_ \"%s: %s is not an array reference\",
${$ALIAS?\q[GvNAME(CvGV(cv))]:\qq[\"$pname\"]},
\"$var\");
OUTPUT
T_LINES
T_ARRAYREF
AV* av = newAV();
$arg = newRV_noinc((SV*)av);
const unsigned int len = $var.size();
av_extend(av, len-1);
for (unsigned int i = 0; i < len; i++) {
av_store(av, i, ${var}[i].to_SV_ref());
av_store(av, i, ${var}[i].to_SV_ref());
}

View File

@ -9,6 +9,8 @@
%typemap{ExtrusionPath*};
%typemap{ExtrusionLoop*};
%typemap{Lines};
%typemap{Polygons};
%typemap{ExPolygons};
%typemap{SurfaceType}{parsed}{
%cpp_type{SurfaceType};