Port offset_ex() and offset2_ex() to XS
This commit is contained in:
parent
9458c7db97
commit
898007fc36
20 changed files with 4349 additions and 52 deletions
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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} } ],
|
||||
) };
|
||||
|
||||
|
|
|
@ -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) ];
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
102
xs/src/ClipperUtils.hpp
Normal 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
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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
3700
xs/src/clipper.cpp
Executable file
File diff suppressed because it is too large
Load diff
349
xs/src/clipper.hpp
Executable file
349
xs/src/clipper.hpp
Executable 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
53
xs/t/11_clipper.t
Normal 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
77
xs/xsp/Clipper.xsp
Normal 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
|
||||
|
||||
%}
|
|
@ -31,7 +31,6 @@ Line::new(...)
|
|||
RETVAL->b.from_SV_check( ST(2) );
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
}
|
||||
|
||||
void
|
||||
Line::rotate(angle, center_sv)
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -9,6 +9,8 @@
|
|||
%typemap{ExtrusionPath*};
|
||||
%typemap{ExtrusionLoop*};
|
||||
%typemap{Lines};
|
||||
%typemap{Polygons};
|
||||
%typemap{ExPolygons};
|
||||
|
||||
%typemap{SurfaceType}{parsed}{
|
||||
%cpp_type{SurfaceType};
|
||||
|
|
Loading…
Add table
Reference in a new issue