diff --git a/README.md b/README.md index d471f6f1..13bd1300 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ The author of the Silk icon set is Mark James. home X axis [G28 X], disable motors [M84]). --layer-gcode Load layer-change G-code from the supplied file (default: nothing). --toolchange-gcode Load tool-change G-code from the supplied file (default: nothing). - --randomize-start Randomize starting point across layers (default: yes) + --seal-position Position of loop starting points (random/nearest/aligned, default: aligned). --external-perimeters-first Reverse perimeter order. (default: no) --spiral-vase Experimental option to raise Z gradually when printing single-walled vases (default: no) @@ -236,10 +236,6 @@ The author of the Silk icon set is Mark James. Quality options (slower slicing): --extra-perimeters Add more perimeters when needed (default: yes) --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) - --start-perimeters-at-concave-points - Try to start perimeters at concave points if any (default: no) - --start-perimeters-at-non-overhang - Try to start perimeters at non-overhang points if any (default: no) --thin-walls Detect single-width walls (default: yes) --overhangs Experimental option to use bridge flow, speed and fan for overhangs (default: yes) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 92a63a61..625e1f8f 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -8,7 +8,8 @@ use List::Util qw(first max); # cemetery of old config settings our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid - rotate scale duplicate_grid); + rotate scale duplicate_grid start_perimeters_at_concave_points start_perimeters_at_non_overhang + randomize_start); our $Options = print_config_def(); @@ -140,6 +141,10 @@ sub _handle_legacy { $value *= 100; $value = "$value"; # force update of the PV value, workaround for bug https://rt.cpan.org/Ticket/Display.html?id=94110 } + if ($opt_key eq 'randomize_start' && $value) { + $opt_key = 'seal_position'; + $value = 'random'; + } # For historical reasons, the world's full of configs having these very low values; # to avoid unexpected behavior we need to ignore them. Banning these two hard-coded diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 5ad1cf1e..73a787d5 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -19,6 +19,7 @@ has '_layer_index' => (is => 'rw', default => sub {-1}); # just a counter has 'layer' => (is => 'rw'); has '_layer_islands' => (is => 'rw'); has '_upper_layer_islands' => (is => 'rw'); +has '_seal_position' => (is => 'ro', default => sub { {} }); # $object => pos has 'shift_x' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} ); has 'z' => (is => 'rw'); @@ -150,45 +151,37 @@ sub extrude_loop { # extrude all loops ccw my $was_clockwise = $loop->make_counter_clockwise; - # find candidate starting points - # start looking for concave vertices not being overhangs - my $polygon = $loop->polygon; - my @concave = (); - if ($self->config->start_perimeters_at_concave_points) { - @concave = $polygon->concave_points; - } - my @candidates = (); - if ($self->config->start_perimeters_at_non_overhang) { - @candidates = grep !$loop->has_overhang_point($_), @concave; - } - if (!@candidates) { - # if none, look for any concave vertex - @candidates = @concave; - if (!@candidates) { - # if none, look for any non-overhang vertex - if ($self->config->start_perimeters_at_non_overhang) { - @candidates = grep !$loop->has_overhang_point($_), @$polygon; - } - if (!@candidates) { - # if none, all points are valid candidates - @candidates = @$polygon; - } - } - } - # find the point of the loop that is closest to the current extruder position # or randomize if requested my $last_pos = $self->last_pos; - if ($self->config->randomize_start && $loop->role == EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER) { - $last_pos = Slic3r::Point->new(scale $self->config->print_center->[X], scale $self->config->bed_size->[Y]); - $last_pos->rotate(rand(2*PI), $self->config->print_center); - } - - # split the loop at the starting point if ($self->config->spiral_vase) { $loop->split_at($last_pos); - } else { - $loop->split_at_vertex($last_pos->nearest_point(\@candidates)); + } elsif ($self->config->seal_position eq 'nearest' || $self->config->seal_position eq 'aligned') { + my $polygon = $loop->polygon; + my @candidates = @{$polygon->concave_points(PI*4/3)}; + @candidates = @{$polygon->convex_points(PI*2/3)} if !@candidates; + @candidates = @{$polygon} if !@candidates; + + my @non_overhang = grep !$loop->has_overhang_point($_), @candidates; + @candidates = @non_overhang if @non_overhang; + + if ($self->config->seal_position eq 'nearest') { + $loop->split_at_vertex($last_pos->nearest_point(\@candidates)); + } elsif ($self->config->seal_position eq 'aligned') { + if (defined $self->layer && defined $self->_seal_position->{$self->layer->object}) { + $last_pos = $self->_seal_position->{$self->layer->object}; + } + my $point = $self->_seal_position->{$self->layer->object} = $last_pos->nearest_point(\@candidates); + $loop->split_at_vertex($point); + } + } elsif ($self->config->seal_position eq 'random') { + if ($loop->role == EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER) { + my $polygon = $loop->polygon; + my $centroid = $polygon->centroid; + $last_pos = Slic3r::Point->new($polygon->bounding_box->x_max, $centroid->y); #)) + $last_pos->rotate(rand(2*PI), $centroid); + } + $loop->split_at($last_pos); } # clip the path to avoid the extruder to get exactly on the first point of the loop; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 9f68b2cd..1d4945f0 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -417,21 +417,17 @@ sub build { }, { title => 'Quality (slower slicing)', - options => [qw(extra_perimeters avoid_crossing_perimeters start_perimeters_at_concave_points start_perimeters_at_non_overhang thin_walls overhangs)], + options => [qw(extra_perimeters avoid_crossing_perimeters thin_walls overhangs)], lines => [ Slic3r::GUI::OptionsGroup->single_option_line('extra_perimeters'), Slic3r::GUI::OptionsGroup->single_option_line('avoid_crossing_perimeters'), - { - label => 'Start perimeters at', - options => [qw(start_perimeters_at_concave_points start_perimeters_at_non_overhang)], - }, Slic3r::GUI::OptionsGroup->single_option_line('thin_walls'), Slic3r::GUI::OptionsGroup->single_option_line('overhangs'), ], }, { title => 'Advanced', - options => [qw(randomize_start external_perimeters_first)], + options => [qw(seal_position external_perimeters_first)], }, ]); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 9a3f17c2..39cec3d6 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -260,12 +260,19 @@ sub make_perimeters { my $role = EXTR_ROLE_PERIMETER; my $loop_role = EXTRL_ROLE_DEFAULT; - if ($is_contour ? $depth == 0 : !@{ $polynode->{children} }) { + + my $root_level = $depth == 0; + my $no_children = !@{ $polynode->{children} }; + my $is_external = $is_contour ? $root_level : $no_children; + my $is_internal = $is_contour ? $no_children : $root_level; + if ($is_external) { # external perimeters are root level in case of contours # and items with no children in case of holes $role = EXTR_ROLE_EXTERNAL_PERIMETER; $loop_role = EXTRL_ROLE_EXTERNAL_PERIMETER; - } elsif ($depth == 1 && $is_contour) { + } elsif ($is_contour && $is_internal) { + # internal perimeters are root level in case of holes + # and items with no children in case of contours $loop_role = EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER; } diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index ee0ec96b..c12d3dca 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -40,15 +40,34 @@ sub subdivide { return Slic3r::Polygon->new(@new_points); } -# for cw polygons this will return convex points! +# angle is checked on the internal side of the polygon sub concave_points { - my $self = shift; + my ($self, $angle) = @_; + + $angle //= PI; my @points = @$self; my @points_pp = @{$self->pp}; - return map $points[$_], - grep Slic3r::Geometry::angle3points(@points_pp[$_, $_-1, $_+1]) < PI - epsilon, - -1 .. ($#points-1); + return [ + map $points[$_], + grep Slic3r::Geometry::angle3points(@points_pp[$_, $_-1, $_+1]) < $angle, + -1 .. ($#points-1) + ]; +} + +# angle is checked on the internal side of the polygon +sub convex_points { + my ($self, $angle) = @_; + + $angle //= PI; + + my @points = @$self; + my @points_pp = @{$self->pp}; + return [ + map $points[$_], + grep Slic3r::Geometry::angle3points(@points_pp[$_, $_-1, $_+1]) > $angle, + -1 .. ($#points-1) + ]; } 1; \ No newline at end of file diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index eeda9304..4e3045cd 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -3,7 +3,7 @@ use strict; use warnings; use List::Util qw(first); -use Slic3r::Geometry qw(X Y epsilon); +use Slic3r::Geometry qw(X Y PI epsilon); use Slic3r::Geometry::Clipper qw(JT_SQUARE); sub new_scale { diff --git a/slic3r.pl b/slic3r.pl index 938f4ed9..aa3a4370 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -342,7 +342,7 @@ $j home X axis [G28 X], disable motors [M84]). --layer-gcode Load layer-change G-code from the supplied file (default: nothing). --toolchange-gcode Load tool-change G-code from the supplied file (default: nothing). - --randomize-start Randomize starting point across layers (default: yes) + --seal-position Position of loop starting points (random/nearest/aligned, default: $config->{seal_position}). --external-perimeters-first Reverse perimeter order. (default: no) --spiral-vase Experimental option to raise Z gradually when printing single-walled vases (default: no) @@ -359,10 +359,6 @@ $j Quality options (slower slicing): --extra-perimeters Add more perimeters when needed (default: yes) --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) - --start-perimeters-at-concave-points - Try to start perimeters at concave points if any (default: no) - --start-perimeters-at-non-overhang - Try to start perimeters at non-overhang points if any (default: no) --thin-walls Detect single-width walls (default: yes) --overhangs Experimental option to use bridge flow, speed and fan for overhangs (default: yes) diff --git a/t/perimeters.t b/t/perimeters.t index 58ebc3ed..239f80f2 100644 --- a/t/perimeters.t +++ b/t/perimeters.t @@ -1,4 +1,4 @@ -use Test::More tests => 10; +use Test::More tests => 9; use strict; use warnings; @@ -85,28 +85,6 @@ use Slic3r::Test; ok !$has_outwards_move, 'move inwards after completing external loop'; } - { - $config->set('start_perimeters_at_concave_points', 1); - my $print = Slic3r::Test::init_print('L', config => $config); - my $loop_starts_from_convex_point = 0; - my $cur_loop; - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($info->{extruding} && $info->{dist_XY} > 0) { - $cur_loop ||= [ [$self->X, $self->Y] ]; - push @$cur_loop, [ @$info{qw(new_X new_Y)} ]; - } else { - if ($cur_loop) { - $loop_starts_from_convex_point = 1 - if Slic3r::Geometry::angle3points(@$cur_loop[0,-1,1]) >= PI; - $cur_loop = undef; - } - } - }); - ok !$loop_starts_from_convex_point, 'avoid starting from convex points'; - } - { $config->set('perimeters', 1); $config->set('perimeter_speed', 77); @@ -267,9 +245,9 @@ use Slic3r::Test; { my $config = Slic3r::Config->new_from_defaults; - $config->set('randomize_start', 1); + $config->set('seal_position', 'random'); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - ok Slic3r::Test::gcode($print), 'successful generation of G-code with randomize_start option'; + ok Slic3r::Test::gcode($print), 'successful generation of G-code with seal_position = random'; } __END__ diff --git a/xs/src/MultiPoint.cpp b/xs/src/MultiPoint.cpp index 51567c5e..47830ce2 100644 --- a/xs/src/MultiPoint.cpp +++ b/xs/src/MultiPoint.cpp @@ -1,7 +1,13 @@ #include "MultiPoint.hpp" +#include "BoundingBox.hpp" namespace Slic3r { +MultiPoint::operator Points() const +{ + return this->points; +} + void MultiPoint::scale(double factor) { @@ -64,6 +70,12 @@ MultiPoint::find_point(const Point &point) const return -1; // not found } +void +MultiPoint::bounding_box(BoundingBox* bb) const +{ + *bb = BoundingBox(this->points); +} + Points MultiPoint::_douglas_peucker(const Points &points, const double tolerance) { diff --git a/xs/src/MultiPoint.hpp b/xs/src/MultiPoint.hpp index c1278aa7..075b0dbd 100644 --- a/xs/src/MultiPoint.hpp +++ b/xs/src/MultiPoint.hpp @@ -2,17 +2,21 @@ #define slic3r_MultiPoint_hpp_ #include -#include "Line.hpp" -#include "Point.hpp" #include #include +#include "Line.hpp" +#include "Point.hpp" namespace Slic3r { +class BoundingBox; + class MultiPoint { public: Points points; + + operator Points() const; void scale(double factor); void translate(double x, double y); void rotate(double angle, const Point ¢er); @@ -23,6 +27,8 @@ class MultiPoint double length() const; bool is_valid() const; int find_point(const Point &point) const; + void bounding_box(BoundingBox* bb) const; + static Points _douglas_peucker(const Points &points, const double tolerance); #ifdef SLIC3RXS diff --git a/xs/src/Polygon.cpp b/xs/src/Polygon.cpp index 948ea230..3fe16bc7 100644 --- a/xs/src/Polygon.cpp +++ b/xs/src/Polygon.cpp @@ -191,6 +191,24 @@ Polygon::triangulate_convex(Polygons* polygons) const } } +// center of mass +Point +Polygon::centroid() const +{ + double area_temp = this->area(); + double x_temp = 0; + double y_temp = 0; + + Polyline polyline; + this->split_at_first_point(&polyline); + for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) { + x_temp += (double)( point->x + (point+1)->x ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y ); + y_temp += (double)( point->y + (point+1)->y ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y ); + } + + return Point(x_temp/(6*area_temp), y_temp/(6*area_temp)); +} + #ifdef SLIC3RXS REGISTER_CLASS(Polygon, "Polygon"); diff --git a/xs/src/Polygon.hpp b/xs/src/Polygon.hpp index b5859e82..816b6be1 100644 --- a/xs/src/Polygon.hpp +++ b/xs/src/Polygon.hpp @@ -35,6 +35,7 @@ class Polygon : public MultiPoint { Polygons simplify(double tolerance) const; void simplify(double tolerance, Polygons &polygons) const; void triangulate_convex(Polygons* polygons) const; + Point centroid() const; #ifdef SLIC3RXS void from_SV_check(SV* poly_sv); diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index d3ced798..913d81de 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -18,6 +18,10 @@ enum SupportMaterialPattern { smpRectilinear, smpRectilinearGrid, smpHoneycomb, smpPillars, }; +enum SealPosition { + spRandom, spNearest, spAligned +}; + template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { t_config_enum_values keys_map; keys_map["reprap"] = gcfRepRap; @@ -50,6 +54,14 @@ template<> inline t_config_enum_values ConfigOptionEnum: return keys_map; } +template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { + t_config_enum_values keys_map; + keys_map["random"] = spRandom; + keys_map["nearest"] = spNearest; + keys_map["aligned"] = spAligned; + return keys_map; +} + class PrintConfigDef { public: @@ -584,11 +596,6 @@ class PrintConfigDef Options["raft_layers"].sidetext = "layers"; Options["raft_layers"].cli = "raft-layers=i"; - Options["randomize_start"].type = coBool; - Options["randomize_start"].label = "Randomize starting points"; - Options["randomize_start"].tooltip = "Start each layer from a different vertex to prevent plastic build-up on the same corner."; - Options["randomize_start"].cli = "randomize-start!"; - Options["resolution"].type = coFloat; Options["resolution"].label = "Resolution"; Options["resolution"].tooltip = "Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input."; @@ -644,6 +651,19 @@ class PrintConfigDef Options["retract_speed"].cli = "retract-speed=f@"; Options["retract_speed"].max = 1000; + Options["seal_position"].type = coEnum; + Options["seal_position"].label = "Seal position"; + Options["seal_position"].category = "Layers and perimeters"; + Options["seal_position"].tooltip = "Position of perimeters starting points."; + Options["seal_position"].cli = "seal-position=s"; + Options["seal_position"].enum_keys_map = ConfigOptionEnum::get_enum_values(); + Options["seal_position"].enum_values.push_back("random"); + Options["seal_position"].enum_values.push_back("nearest"); + Options["seal_position"].enum_values.push_back("aligned"); + Options["seal_position"].enum_labels.push_back("Random"); + Options["seal_position"].enum_labels.push_back("Nearest"); + Options["seal_position"].enum_labels.push_back("Aligned"); + Options["skirt_distance"].type = coFloat; Options["skirt_distance"].label = "Distance from object"; Options["skirt_distance"].tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion."; @@ -753,16 +773,6 @@ class PrintConfigDef Options["start_gcode"].full_width = true; Options["start_gcode"].height = 120; - Options["start_perimeters_at_concave_points"].type = coBool; - Options["start_perimeters_at_concave_points"].label = "Concave points"; - Options["start_perimeters_at_concave_points"].tooltip = "Prefer to start perimeters at a concave point."; - Options["start_perimeters_at_concave_points"].cli = "start-perimeters-at-concave-points!"; - - Options["start_perimeters_at_non_overhang"].type = coBool; - Options["start_perimeters_at_non_overhang"].label = "Non-overhang points"; - Options["start_perimeters_at_non_overhang"].tooltip = "Prefer to start perimeters at non-overhanging points."; - Options["start_perimeters_at_non_overhang"].cli = "start-perimeters-at-non-overhang!"; - Options["support_material"].type = coBool; Options["support_material"].label = "Generate support material"; Options["support_material"].category = "Support material"; @@ -996,6 +1006,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionBool interface_shells; ConfigOptionFloat layer_height; ConfigOptionInt raft_layers; + ConfigOptionEnum seal_position; ConfigOptionBool support_material; ConfigOptionInt support_material_angle; ConfigOptionInt support_material_enforce_layers; @@ -1020,6 +1031,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig this->interface_shells.value = false; this->layer_height.value = 0.4; this->raft_layers.value = 0; + this->seal_position.value = spAligned; this->support_material.value = false; this->support_material_angle.value = 0; this->support_material_enforce_layers.value = 0; @@ -1045,6 +1057,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig if (opt_key == "interface_shells") return &this->interface_shells; if (opt_key == "layer_height") return &this->layer_height; if (opt_key == "raft_layers") return &this->raft_layers; + if (opt_key == "seal_position") return &this->seal_position; if (opt_key == "support_material") return &this->support_material; if (opt_key == "support_material_angle") return &this->support_material_angle; if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers; @@ -1214,7 +1227,6 @@ class PrintConfig : public virtual StaticPrintConfig ConfigOptionFloat perimeter_acceleration; ConfigOptionStrings post_process; ConfigOptionPoint print_center; - ConfigOptionBool randomize_start; ConfigOptionFloat resolution; ConfigOptionFloats retract_before_travel; ConfigOptionBools retract_layer_change; @@ -1231,8 +1243,6 @@ class PrintConfig : public virtual StaticPrintConfig ConfigOptionBool spiral_vase; ConfigOptionInt standby_temperature_delta; ConfigOptionString start_gcode; - ConfigOptionBool start_perimeters_at_concave_points; - ConfigOptionBool start_perimeters_at_non_overhang; ConfigOptionInts temperature; ConfigOptionInt threads; ConfigOptionString toolchange_gcode; @@ -1296,7 +1306,6 @@ class PrintConfig : public virtual StaticPrintConfig this->output_filename_format.value = "[input_filename_base].gcode"; this->perimeter_acceleration.value = 0; this->print_center.point = Pointf(100,100); - this->randomize_start.value = false; this->resolution.value = 0; this->retract_before_travel.values.resize(1); this->retract_before_travel.values[0] = 2; @@ -1321,8 +1330,6 @@ class PrintConfig : public virtual StaticPrintConfig this->spiral_vase.value = false; this->standby_temperature_delta.value = -5; this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"; - this->start_perimeters_at_concave_points.value = false; - this->start_perimeters_at_non_overhang.value = false; this->temperature.values.resize(1); this->temperature.values[0] = 200; this->threads.value = 2; @@ -1383,7 +1390,6 @@ class PrintConfig : public virtual StaticPrintConfig if (opt_key == "perimeter_acceleration") return &this->perimeter_acceleration; if (opt_key == "post_process") return &this->post_process; if (opt_key == "print_center") return &this->print_center; - if (opt_key == "randomize_start") return &this->randomize_start; if (opt_key == "resolution") return &this->resolution; if (opt_key == "retract_before_travel") return &this->retract_before_travel; if (opt_key == "retract_layer_change") return &this->retract_layer_change; @@ -1400,8 +1406,6 @@ class PrintConfig : public virtual StaticPrintConfig if (opt_key == "spiral_vase") return &this->spiral_vase; if (opt_key == "standby_temperature_delta") return &this->standby_temperature_delta; if (opt_key == "start_gcode") return &this->start_gcode; - if (opt_key == "start_perimeters_at_concave_points") return &this->start_perimeters_at_concave_points; - if (opt_key == "start_perimeters_at_non_overhang") return &this->start_perimeters_at_non_overhang; if (opt_key == "temperature") return &this->temperature; if (opt_key == "threads") return &this->threads; if (opt_key == "toolchange_gcode") return &this->toolchange_gcode; diff --git a/xs/t/06_polygon.t b/xs/t/06_polygon.t index 85863888..116919a7 100644 --- a/xs/t/06_polygon.t +++ b/xs/t/06_polygon.t @@ -5,7 +5,7 @@ use warnings; use List::Util qw(first); use Slic3r::XS; -use Test::More tests => 19; +use Test::More tests => 20; use constant PI => 4 * atan2(1, 1); @@ -69,6 +69,10 @@ ok $cw_polygon->contains_point(Slic3r::Point->new(150,150)), 'cw contains_point' ok !(defined first { $_->is_clockwise } @$triangles), 'all triangles are ccw'; } +{ + is_deeply $polygon->centroid->pp, [150,150], 'centroid'; +} + # this is not a test: this just demonstrates bad usage, where $polygon->clone gets # DESTROY'ed before the derived object ($point), causing bad memory access if (0) { diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index c6f0db1d..489bca03 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -64,7 +64,7 @@ _constant() ALIAS: EXTRL_ROLE_DEFAULT = elrDefault EXTRL_ROLE_EXTERNAL_PERIMETER = elrExternalPerimeter - EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER = erInternalInfill + EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER = elrContourInternalPerimeter PROTOTYPE: CODE: RETVAL = ix; diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index c7b10846..11fd0436 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -38,6 +38,12 @@ Polygons simplify(double tolerance); Polygons triangulate_convex() %code{% THIS->triangulate_convex(&RETVAL); %}; + Clone centroid(); + BoundingBox* bounding_box() + %code{% + RETVAL = new BoundingBox(); + THIS->bounding_box(RETVAL); + %}; %{ Polygon*