Ported Slic3r::BridgeDetector to XS
This commit is contained in:
parent
36825e0134
commit
379cde30e2
@ -58,7 +58,6 @@ use Slic3r::GCode::VibrationLimit;
|
|||||||
use Slic3r::Geometry qw(PI);
|
use Slic3r::Geometry qw(PI);
|
||||||
use Slic3r::Geometry::Clipper;
|
use Slic3r::Geometry::Clipper;
|
||||||
use Slic3r::Layer;
|
use Slic3r::Layer;
|
||||||
use Slic3r::Layer::BridgeDetector;
|
|
||||||
use Slic3r::Layer::Region;
|
use Slic3r::Layer::Region;
|
||||||
use Slic3r::Line;
|
use Slic3r::Line;
|
||||||
use Slic3r::Model;
|
use Slic3r::Model;
|
||||||
@ -162,6 +161,7 @@ sub thread_cleanup {
|
|||||||
|
|
||||||
# prevent destruction of shared objects
|
# prevent destruction of shared objects
|
||||||
no warnings 'redefine';
|
no warnings 'redefine';
|
||||||
|
*Slic3r::BridgeDetector::DESTROY = sub {};
|
||||||
*Slic3r::Config::DESTROY = sub {};
|
*Slic3r::Config::DESTROY = sub {};
|
||||||
*Slic3r::Config::Full::DESTROY = sub {};
|
*Slic3r::Config::Full::DESTROY = sub {};
|
||||||
*Slic3r::Config::GCode::DESTROY = sub {};
|
*Slic3r::Config::GCode::DESTROY = sub {};
|
||||||
|
@ -207,23 +207,6 @@ sub polygon_is_convex {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub deg2rad {
|
|
||||||
my ($degrees) = @_;
|
|
||||||
return PI() * $degrees / 180;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub rad2deg {
|
|
||||||
my ($rad) = @_;
|
|
||||||
return $rad / PI() * 180;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub rad2deg_dir {
|
|
||||||
my ($rad) = @_;
|
|
||||||
$rad = ($rad < PI) ? (-$rad + PI/2) : ($rad + PI/2);
|
|
||||||
$rad += PI if $rad < 0;
|
|
||||||
return rad2deg($rad);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub rotate_points {
|
sub rotate_points {
|
||||||
my ($radians, $center, @points) = @_;
|
my ($radians, $center, @points) = @_;
|
||||||
$center //= [0,0];
|
$center //= [0,0];
|
||||||
|
@ -1,277 +0,0 @@
|
|||||||
package Slic3r::Layer::BridgeDetector;
|
|
||||||
use Moo;
|
|
||||||
|
|
||||||
use List::Util qw(first sum max min);
|
|
||||||
use Slic3r::Geometry qw(PI unscale scaled_epsilon rad2deg epsilon directions_parallel_within);
|
|
||||||
use Slic3r::Geometry::Clipper qw(intersection_pl intersection_ex union offset diff_pl union_ex
|
|
||||||
intersection_ppl);
|
|
||||||
|
|
||||||
has 'expolygon' => (is => 'ro', required => 1);
|
|
||||||
has 'lower_slices' => (is => 'rw', required => 1); # ExPolygons or ExPolygonCollection
|
|
||||||
has 'extrusion_width' => (is => 'rw', required => 1); # scaled
|
|
||||||
has 'resolution' => (is => 'rw', default => sub { PI/36 });
|
|
||||||
|
|
||||||
has '_edges' => (is => 'rw'); # Polylines representing the supporting edges
|
|
||||||
has '_anchors' => (is => 'rw'); # ExPolygons
|
|
||||||
has 'angle' => (is => 'rw');
|
|
||||||
|
|
||||||
sub BUILD {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
# outset our bridge by an arbitrary amout; we'll use this outer margin
|
|
||||||
# for detecting anchors
|
|
||||||
my $grown = $self->expolygon->offset(+$self->extrusion_width);
|
|
||||||
|
|
||||||
# detect what edges lie on lower slices
|
|
||||||
$self->_edges(my $edges = []);
|
|
||||||
foreach my $lower (@{$self->lower_slices}) {
|
|
||||||
# turn bridge contour and holes into polylines and then clip them
|
|
||||||
# with each lower slice's contour
|
|
||||||
push @$edges, @{intersection_ppl($grown, [ $lower->contour ])};
|
|
||||||
}
|
|
||||||
Slic3r::debugf " bridge has %d support(s)\n", scalar(@$edges);
|
|
||||||
|
|
||||||
# detect anchors as intersection between our bridge expolygon and the lower slices
|
|
||||||
$self->_anchors(intersection_ex(
|
|
||||||
$grown,
|
|
||||||
[ map @$_, @{$self->lower_slices} ],
|
|
||||||
1, # safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some @edges
|
|
||||||
));
|
|
||||||
|
|
||||||
if (0) {
|
|
||||||
require "Slic3r/SVG.pm";
|
|
||||||
Slic3r::SVG::output("bridge.svg",
|
|
||||||
expolygons => [ $self->expolygon ],
|
|
||||||
red_expolygons => $self->lower_slices,
|
|
||||||
polylines => $self->_edges,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub detect_angle {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
return undef if !@{$self->_edges};
|
|
||||||
|
|
||||||
my @edges = @{$self->_edges};
|
|
||||||
my $anchors = $self->_anchors;
|
|
||||||
|
|
||||||
if (!@$anchors) {
|
|
||||||
$self->angle(undef);
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Outset the bridge expolygon by half the amount we used for detecting anchors;
|
|
||||||
# we'll use this one to clip our test lines and be sure that their endpoints
|
|
||||||
# are inside the anchors and not on their contours leading to false negatives.
|
|
||||||
my $clip_area = $self->expolygon->offset_ex(+$self->extrusion_width/2);
|
|
||||||
|
|
||||||
# we'll now try several directions using a rudimentary visibility check:
|
|
||||||
# bridge in several directions and then sum the length of lines having both
|
|
||||||
# endpoints within anchors
|
|
||||||
|
|
||||||
# we test angles according to configured resolution
|
|
||||||
my @angles = map { $_*$self->resolution } 0..(PI/$self->resolution);
|
|
||||||
|
|
||||||
# we also test angles of each bridge contour
|
|
||||||
push @angles, map $_->direction, map @{$_->lines}, @{$self->expolygon};
|
|
||||||
|
|
||||||
# we also test angles of each open supporting edge
|
|
||||||
# (this finds the optimal angle for C-shaped supports)
|
|
||||||
push @angles,
|
|
||||||
map Slic3r::Line->new($_->first_point, $_->last_point)->direction,
|
|
||||||
grep { !$_->first_point->coincides_with($_->last_point) }
|
|
||||||
@edges;
|
|
||||||
|
|
||||||
# remove duplicates
|
|
||||||
my $min_resolution = PI/180; # 1 degree
|
|
||||||
# proceed in reverse order so that when we compare first value with last one (-1)
|
|
||||||
# we remove the greatest one (PI) in case they are parallel (PI, 0)
|
|
||||||
@angles = reverse sort @angles;
|
|
||||||
for (my $i = 0; $i <= $#angles; ++$i) {
|
|
||||||
if (directions_parallel_within($angles[$i], $angles[$i-1], $min_resolution)) {
|
|
||||||
splice @angles, $i, 1;
|
|
||||||
--$i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my %directions_coverage = (); # angle => score
|
|
||||||
my %directions_avg_length = (); # angle => score
|
|
||||||
my $line_increment = $self->extrusion_width;
|
|
||||||
my %unique_angles = map { $_ => 1 } @angles;
|
|
||||||
for my $angle (@angles) {
|
|
||||||
my $my_clip_area = [ map $_->clone, @$clip_area ];
|
|
||||||
my $my_anchors = [ map $_->clone, @$anchors ];
|
|
||||||
|
|
||||||
# rotate everything - the center point doesn't matter
|
|
||||||
$_->rotate(-$angle, [0,0]) for @$my_clip_area, @$my_anchors;
|
|
||||||
|
|
||||||
# generate lines in this direction
|
|
||||||
my $bounding_box = Slic3r::Geometry::BoundingBox->new_from_points([ map @$_, map @$_, @$my_anchors ]);
|
|
||||||
|
|
||||||
my @lines = ();
|
|
||||||
for (my $y = $bounding_box->y_min; $y <= $bounding_box->y_max; $y+= $line_increment) {
|
|
||||||
push @lines, Slic3r::Polyline->new(
|
|
||||||
[$bounding_box->x_min, $y],
|
|
||||||
[$bounding_box->x_max, $y],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
my @clipped_lines = map Slic3r::Line->new(@$_), @{ intersection_pl(\@lines, [ map @$_, @$my_clip_area ]) };
|
|
||||||
|
|
||||||
# remove any line not having both endpoints within anchors
|
|
||||||
@clipped_lines = grep {
|
|
||||||
my $line = $_;
|
|
||||||
(first { $_->contains_point($line->a) } @$my_anchors)
|
|
||||||
&& (first { $_->contains_point($line->b) } @$my_anchors);
|
|
||||||
} @clipped_lines;
|
|
||||||
|
|
||||||
my @lengths = map $_->length, @clipped_lines;
|
|
||||||
|
|
||||||
# sum length of bridged lines
|
|
||||||
$directions_coverage{$angle} = sum(@lengths) // 0;
|
|
||||||
|
|
||||||
### The following produces more correct results in some cases and more broken in others.
|
|
||||||
### TODO: investigate, as it looks more reliable than line clipping.
|
|
||||||
###$directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0;
|
|
||||||
|
|
||||||
# max length of bridged lines
|
|
||||||
$directions_avg_length{$angle} = @lengths ? (max(@lengths)) : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
# if no direction produced coverage, then there's no bridge direction
|
|
||||||
return undef if !defined first { $_ > 0 } values %directions_coverage;
|
|
||||||
|
|
||||||
# the best direction is the one causing most lines to be bridged (thus most coverage)
|
|
||||||
# and shortest max line length
|
|
||||||
my @sorted_directions = sort {
|
|
||||||
my $cmp;
|
|
||||||
my $coverage_diff = $directions_coverage{$a} - $directions_coverage{$b};
|
|
||||||
if (abs($coverage_diff) < $self->extrusion_width) {
|
|
||||||
$cmp = $directions_avg_length{$b} <=> $directions_avg_length{$a};
|
|
||||||
} else {
|
|
||||||
$cmp = ($coverage_diff > 0) ? 1 : -1;
|
|
||||||
}
|
|
||||||
$cmp;
|
|
||||||
} keys %directions_coverage;
|
|
||||||
|
|
||||||
$self->angle($sorted_directions[-1]);
|
|
||||||
|
|
||||||
if ($self->angle >= PI) {
|
|
||||||
$self->angle($self->angle - PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
Slic3r::debugf " Optimal infill angle is %d degrees\n", rad2deg($self->angle);
|
|
||||||
|
|
||||||
return $self->angle;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub coverage {
|
|
||||||
my ($self, $angle) = @_;
|
|
||||||
|
|
||||||
if (!defined $angle) {
|
|
||||||
return [] if !defined($angle = $self->angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Clone our expolygon and rotate it so that we work with vertical lines.
|
|
||||||
my $expolygon = $self->expolygon->clone;
|
|
||||||
$expolygon->rotate(PI/2 - $angle, [0,0]);
|
|
||||||
|
|
||||||
# Outset the bridge expolygon by half the amount we used for detecting anchors;
|
|
||||||
# we'll use this one to generate our trapezoids and be sure that their vertices
|
|
||||||
# are inside the anchors and not on their contours leading to false negatives.
|
|
||||||
my $grown = $expolygon->offset_ex(+$self->extrusion_width/2);
|
|
||||||
|
|
||||||
# Compute trapezoids according to a vertical orientation
|
|
||||||
my $trapezoids = [ map @{$_->get_trapezoids2(PI/2)}, @$grown ];
|
|
||||||
|
|
||||||
# get anchors and rotate them too
|
|
||||||
my $anchors = [ map $_->clone, @{$self->_anchors} ];
|
|
||||||
$_->rotate(PI/2 - $angle, [0,0]) for @$anchors;
|
|
||||||
|
|
||||||
my @covered = (); # polygons
|
|
||||||
foreach my $trapezoid (@$trapezoids) {
|
|
||||||
my @polylines = map $_->as_polyline, @{$trapezoid->lines};
|
|
||||||
my @supported = @{intersection_pl(\@polylines, [map @$_, @$anchors])};
|
|
||||||
|
|
||||||
# not nice, we need a more robust non-numeric check
|
|
||||||
@supported = grep $_->length >= $self->extrusion_width, @supported;
|
|
||||||
|
|
||||||
if (@supported >= 2) {
|
|
||||||
push @covered, $trapezoid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# merge trapezoids and rotate them back
|
|
||||||
my $coverage = union(\@covered);
|
|
||||||
$_->rotate(-(PI/2 - $angle), [0,0]) for @$coverage;
|
|
||||||
|
|
||||||
# intersect trapezoids with actual bridge area to remove extra margins
|
|
||||||
$coverage = intersection_ex($coverage, [ @{$self->expolygon} ]);
|
|
||||||
|
|
||||||
if (0) {
|
|
||||||
my @lines = map @{$_->lines}, @$trapezoids;
|
|
||||||
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
|
|
||||||
|
|
||||||
require "Slic3r/SVG.pm";
|
|
||||||
Slic3r::SVG::output(
|
|
||||||
"coverage_" . rad2deg($angle) . ".svg",
|
|
||||||
expolygons => [$self->expolygon],
|
|
||||||
green_expolygons => $self->_anchors,
|
|
||||||
red_expolygons => $coverage,
|
|
||||||
lines => \@lines,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $coverage;
|
|
||||||
}
|
|
||||||
|
|
||||||
# this method returns the bridge edges (as polylines) that are not supported
|
|
||||||
# but would allow the entire bridge area to be bridged with detected angle
|
|
||||||
# if supported too
|
|
||||||
sub unsupported_edges {
|
|
||||||
my ($self, $angle) = @_;
|
|
||||||
|
|
||||||
if (!defined $angle) {
|
|
||||||
return [] if !defined($angle = $self->angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
# get bridge edges (both contour and holes)
|
|
||||||
my @bridge_edges = map $_->split_at_first_point, @{$self->expolygon};
|
|
||||||
$_->[0]->translate(1,0) for @bridge_edges; # workaround for Clipper bug, see comments in Slic3r::Polygon::clip_as_polyline()
|
|
||||||
|
|
||||||
# get unsupported edges
|
|
||||||
my $grown_lower = offset([ map @$_, @{$self->lower_slices} ], +$self->extrusion_width);
|
|
||||||
my $unsupported = diff_pl(
|
|
||||||
\@bridge_edges,
|
|
||||||
$grown_lower,
|
|
||||||
);
|
|
||||||
|
|
||||||
# split into individual segments and filter out edges parallel to the bridging angle
|
|
||||||
# TODO: angle tolerance should probably be based on segment length and flow width,
|
|
||||||
# so that we build supports whenever there's a chance that at least one or two bridge
|
|
||||||
# extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
|
|
||||||
# direction might still benefit from anchors if long enough)
|
|
||||||
my $angle_tolerance = PI/180*5;
|
|
||||||
@$unsupported = map $_->as_polyline,
|
|
||||||
grep !directions_parallel_within($_->direction, $angle, $angle_tolerance),
|
|
||||||
map @{$_->lines},
|
|
||||||
@$unsupported;
|
|
||||||
|
|
||||||
if (0) {
|
|
||||||
require "Slic3r/SVG.pm";
|
|
||||||
Slic3r::SVG::output(
|
|
||||||
"unsupported_" . rad2deg($angle) . ".svg",
|
|
||||||
expolygons => [$self->expolygon],
|
|
||||||
green_expolygons => $self->_anchors,
|
|
||||||
red_expolygons => union_ex($grown_lower),
|
|
||||||
no_arrows => 1,
|
|
||||||
polylines => \@bridge_edges,
|
|
||||||
red_polylines => $unsupported,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $unsupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
@ -486,16 +486,17 @@ sub process_external_surfaces {
|
|||||||
# of very thin (but still working) anchors, the grown expolygon would go beyond them
|
# of very thin (but still working) anchors, the grown expolygon would go beyond them
|
||||||
my $angle;
|
my $angle;
|
||||||
if ($lower_layer) {
|
if ($lower_layer) {
|
||||||
my $bridge_detector = Slic3r::Layer::BridgeDetector->new(
|
my $bridge_detector = Slic3r::BridgeDetector->new(
|
||||||
expolygon => $surface->expolygon,
|
$surface->expolygon,
|
||||||
lower_slices => $lower_layer->slices,
|
$lower_layer->slices,
|
||||||
extrusion_width => $self->flow(FLOW_ROLE_INFILL, $self->height, 1)->scaled_width,
|
$self->flow(FLOW_ROLE_INFILL, $self->height, 1)->scaled_width,
|
||||||
);
|
);
|
||||||
Slic3r::debugf "Processing bridge at layer %d:\n", $self->id;
|
Slic3r::debugf "Processing bridge at layer %d:\n", $self->id;
|
||||||
$angle = $bridge_detector->detect_angle;
|
$bridge_detector->detect_angle;
|
||||||
|
$angle = $bridge_detector->angle;
|
||||||
|
|
||||||
if (defined $angle && $self->object->config->support_material) {
|
if (defined $angle && $self->object->config->support_material) {
|
||||||
$self->bridged->append($_) for @{ $bridge_detector->coverage($angle) };
|
$self->bridged->append($_) for @{ $bridge_detector->coverage_with_angle($angle) };
|
||||||
$self->unsupported_bridge_edges->append($_) for @{ $bridge_detector->unsupported_edges };
|
$self->unsupported_bridge_edges->append($_) for @{ $bridge_detector->unsupported_edges };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
t/bridges.t
13
t/bridges.t
@ -84,17 +84,18 @@ use Slic3r::Test;
|
|||||||
sub check_angle {
|
sub check_angle {
|
||||||
my ($lower, $bridge, $expected, $tolerance, $expected_coverage) = @_;
|
my ($lower, $bridge, $expected, $tolerance, $expected_coverage) = @_;
|
||||||
|
|
||||||
|
if (ref($lower) eq 'ARRAY') {
|
||||||
|
$lower = Slic3r::ExPolygon::Collection->new(@$lower);
|
||||||
|
}
|
||||||
|
|
||||||
$expected_coverage //= -1;
|
$expected_coverage //= -1;
|
||||||
$expected_coverage = $bridge->area if $expected_coverage == -1;
|
$expected_coverage = $bridge->area if $expected_coverage == -1;
|
||||||
|
|
||||||
my $bd = Slic3r::Layer::BridgeDetector->new(
|
my $bd = Slic3r::BridgeDetector->new($bridge, $lower, scale 0.5);
|
||||||
expolygon => $bridge,
|
|
||||||
lower_slices => $lower,
|
|
||||||
extrusion_width => scale 0.5,
|
|
||||||
);
|
|
||||||
|
|
||||||
$tolerance //= rad2deg($bd->resolution) + epsilon;
|
$tolerance //= rad2deg($bd->resolution) + epsilon;
|
||||||
my $result = $bd->detect_angle;
|
$bd->detect_angle;
|
||||||
|
my $result = $bd->angle;
|
||||||
my $coverage = $bd->coverage;
|
my $coverage = $bd->coverage;
|
||||||
is sum(map $_->area, @$coverage), $expected_coverage, 'correct coverage area';
|
is sum(map $_->area, @$coverage), $expected_coverage, 'correct coverage area';
|
||||||
|
|
||||||
|
@ -1652,6 +1652,8 @@ src/clipper.cpp
|
|||||||
src/clipper.hpp
|
src/clipper.hpp
|
||||||
src/libslic3r/BoundingBox.cpp
|
src/libslic3r/BoundingBox.cpp
|
||||||
src/libslic3r/BoundingBox.hpp
|
src/libslic3r/BoundingBox.hpp
|
||||||
|
src/libslic3r/BridgeDetector.cpp
|
||||||
|
src/libslic3r/BridgeDetector.hpp
|
||||||
src/libslic3r/ClipperUtils.cpp
|
src/libslic3r/ClipperUtils.cpp
|
||||||
src/libslic3r/ClipperUtils.hpp
|
src/libslic3r/ClipperUtils.hpp
|
||||||
src/libslic3r/Config.cpp
|
src/libslic3r/Config.cpp
|
||||||
@ -1669,6 +1671,8 @@ src/libslic3r/ExtrusionEntityCollection.hpp
|
|||||||
src/libslic3r/Flow.cpp
|
src/libslic3r/Flow.cpp
|
||||||
src/libslic3r/Flow.hpp
|
src/libslic3r/Flow.hpp
|
||||||
src/libslic3r/GCode.hpp
|
src/libslic3r/GCode.hpp
|
||||||
|
src/libslic3r/GCodeWriter.cpp
|
||||||
|
src/libslic3r/GCodeWriter.hpp
|
||||||
src/libslic3r/Geometry.cpp
|
src/libslic3r/Geometry.cpp
|
||||||
src/libslic3r/Geometry.hpp
|
src/libslic3r/Geometry.hpp
|
||||||
src/libslic3r/Layer.cpp
|
src/libslic3r/Layer.cpp
|
||||||
@ -1745,6 +1749,7 @@ t/18_motionplanner.t
|
|||||||
t/19_model.t
|
t/19_model.t
|
||||||
t/20_print.t
|
t/20_print.t
|
||||||
xsp/BoundingBox.xsp
|
xsp/BoundingBox.xsp
|
||||||
|
xsp/BridgeDetector.xsp
|
||||||
xsp/Clipper.xsp
|
xsp/Clipper.xsp
|
||||||
xsp/Config.xsp
|
xsp/Config.xsp
|
||||||
xsp/ExPolygon.xsp
|
xsp/ExPolygon.xsp
|
||||||
@ -1754,6 +1759,7 @@ xsp/ExtrusionEntityCollection.xsp
|
|||||||
xsp/ExtrusionLoop.xsp
|
xsp/ExtrusionLoop.xsp
|
||||||
xsp/ExtrusionPath.xsp
|
xsp/ExtrusionPath.xsp
|
||||||
xsp/Flow.xsp
|
xsp/Flow.xsp
|
||||||
|
xsp/GCodeWriter.xsp
|
||||||
xsp/Geometry.xsp
|
xsp/Geometry.xsp
|
||||||
xsp/Layer.xsp
|
xsp/Layer.xsp
|
||||||
xsp/Line.xsp
|
xsp/Line.xsp
|
||||||
|
@ -188,6 +188,7 @@ sub new {
|
|||||||
|
|
||||||
package main;
|
package main;
|
||||||
for my $class (qw(
|
for my $class (qw(
|
||||||
|
Slic3r::BridgeDetector
|
||||||
Slic3r::Config
|
Slic3r::Config
|
||||||
Slic3r::Config::Full
|
Slic3r::Config::Full
|
||||||
Slic3r::Config::GCode
|
Slic3r::Config::GCode
|
||||||
|
@ -94,6 +94,14 @@ BoundingBoxBase<PointClass>::merge(const PointClass &point)
|
|||||||
template void BoundingBoxBase<Point>::merge(const Point &point);
|
template void BoundingBoxBase<Point>::merge(const Point &point);
|
||||||
template void BoundingBoxBase<Pointf>::merge(const Pointf &point);
|
template void BoundingBoxBase<Pointf>::merge(const Pointf &point);
|
||||||
|
|
||||||
|
template <class PointClass> void
|
||||||
|
BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
|
||||||
|
{
|
||||||
|
this->merge(BoundingBoxBase(points));
|
||||||
|
}
|
||||||
|
template void BoundingBoxBase<Point>::merge(const Points &points);
|
||||||
|
template void BoundingBoxBase<Pointf>::merge(const Pointfs &points);
|
||||||
|
|
||||||
template <class PointClass> void
|
template <class PointClass> void
|
||||||
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
|
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
|
||||||
{
|
{
|
||||||
@ -122,6 +130,13 @@ BoundingBox3Base<PointClass>::merge(const PointClass &point)
|
|||||||
}
|
}
|
||||||
template void BoundingBox3Base<Pointf3>::merge(const Pointf3 &point);
|
template void BoundingBox3Base<Pointf3>::merge(const Pointf3 &point);
|
||||||
|
|
||||||
|
template <class PointClass> void
|
||||||
|
BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points)
|
||||||
|
{
|
||||||
|
this->merge(BoundingBox3Base(points));
|
||||||
|
}
|
||||||
|
template void BoundingBox3Base<Pointf3>::merge(const Pointf3s &points);
|
||||||
|
|
||||||
template <class PointClass> void
|
template <class PointClass> void
|
||||||
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
|
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,7 @@ class BoundingBoxBase
|
|||||||
BoundingBoxBase() : defined(false) {};
|
BoundingBoxBase() : defined(false) {};
|
||||||
BoundingBoxBase(const std::vector<PointClass> &points);
|
BoundingBoxBase(const std::vector<PointClass> &points);
|
||||||
void merge(const PointClass &point);
|
void merge(const PointClass &point);
|
||||||
|
void merge(const std::vector<PointClass> &points);
|
||||||
void merge(const BoundingBoxBase<PointClass> &bb);
|
void merge(const BoundingBoxBase<PointClass> &bb);
|
||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
PointClass size() const;
|
PointClass size() const;
|
||||||
@ -38,6 +39,7 @@ class BoundingBox3Base : public BoundingBoxBase<PointClass>
|
|||||||
BoundingBox3Base() : BoundingBoxBase<PointClass>() {};
|
BoundingBox3Base() : BoundingBoxBase<PointClass>() {};
|
||||||
BoundingBox3Base(const std::vector<PointClass> &points);
|
BoundingBox3Base(const std::vector<PointClass> &points);
|
||||||
void merge(const PointClass &point);
|
void merge(const PointClass &point);
|
||||||
|
void merge(const std::vector<PointClass> &points);
|
||||||
void merge(const BoundingBox3Base<PointClass> &bb);
|
void merge(const BoundingBox3Base<PointClass> &bb);
|
||||||
PointClass size() const;
|
PointClass size() const;
|
||||||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||||
|
331
xs/src/libslic3r/BridgeDetector.cpp
Normal file
331
xs/src/libslic3r/BridgeDetector.cpp
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
#include "BridgeDetector.hpp"
|
||||||
|
#include "ClipperUtils.hpp"
|
||||||
|
#include "Geometry.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class BridgeDirectionComparator {
|
||||||
|
public:
|
||||||
|
std::map<double,double> dir_coverage, dir_avg_length; // angle => score
|
||||||
|
|
||||||
|
BridgeDirectionComparator(double _extrusion_width)
|
||||||
|
: extrusion_width(_extrusion_width) {};
|
||||||
|
|
||||||
|
// the best direction is the one causing most lines to be bridged (thus most coverage)
|
||||||
|
// and shortest max line length
|
||||||
|
bool operator() (double a, double b) {
|
||||||
|
double coverage_diff = this->dir_coverage[a] - this->dir_coverage[b];
|
||||||
|
if (fabs(coverage_diff) < this->extrusion_width) {
|
||||||
|
return (this->dir_avg_length[b] > this->dir_avg_length[a]);
|
||||||
|
} else {
|
||||||
|
return (coverage_diff > 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
double extrusion_width;
|
||||||
|
};
|
||||||
|
|
||||||
|
BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices,
|
||||||
|
coord_t _extrusion_width)
|
||||||
|
: expolygon(_expolygon), lower_slices(_lower_slices), extrusion_width(_extrusion_width),
|
||||||
|
angle(-1), resolution(PI/36.0)
|
||||||
|
{
|
||||||
|
/* outset our bridge by an arbitrary amout; we'll use this outer margin
|
||||||
|
for detecting anchors */
|
||||||
|
Polygons grown;
|
||||||
|
offset((Polygons)this->expolygon, grown, this->extrusion_width);
|
||||||
|
|
||||||
|
// detect what edges lie on lower slices
|
||||||
|
for (ExPolygons::const_iterator lower = this->lower_slices.expolygons.begin();
|
||||||
|
lower != this->lower_slices.expolygons.end();
|
||||||
|
++lower) {
|
||||||
|
/* turn bridge contour and holes into polylines and then clip them
|
||||||
|
with each lower slice's contour */
|
||||||
|
intersection(grown, lower->contour, this->_edges);
|
||||||
|
}
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
printf(" bridge has %zu support(s)\n", this->_edges.size());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// detect anchors as intersection between our bridge expolygon and the lower slices
|
||||||
|
// safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges
|
||||||
|
intersection(grown, this->lower_slices, this->_anchors, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (0) {
|
||||||
|
require "Slic3r/SVG.pm";
|
||||||
|
Slic3r::SVG::output("bridge.svg",
|
||||||
|
expolygons => [ $self->expolygon ],
|
||||||
|
red_expolygons => $self->lower_slices,
|
||||||
|
polylines => $self->_edges,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BridgeDetector::detect_angle()
|
||||||
|
{
|
||||||
|
if (this->_edges.empty() || this->_anchors.empty()) return false;
|
||||||
|
|
||||||
|
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
|
||||||
|
we'll use this one to clip our test lines and be sure that their endpoints
|
||||||
|
are inside the anchors and not on their contours leading to false negatives. */
|
||||||
|
Polygons clip_area;
|
||||||
|
offset(this->expolygon, clip_area, +this->extrusion_width/2);
|
||||||
|
|
||||||
|
/* we'll now try several directions using a rudimentary visibility check:
|
||||||
|
bridge in several directions and then sum the length of lines having both
|
||||||
|
endpoints within anchors */
|
||||||
|
|
||||||
|
// we test angles according to configured resolution
|
||||||
|
std::vector<double> angles;
|
||||||
|
for (int i = 0; i <= PI/this->resolution; ++i)
|
||||||
|
angles.push_back(i * this->resolution);
|
||||||
|
|
||||||
|
// we also test angles of each bridge contour
|
||||||
|
{
|
||||||
|
Polygons pp = this->expolygon;
|
||||||
|
for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) {
|
||||||
|
Lines lines;
|
||||||
|
p->lines(&lines);
|
||||||
|
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
|
||||||
|
angles.push_back(line->direction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we also test angles of each open supporting edge
|
||||||
|
(this finds the optimal angle for C-shaped supports) */
|
||||||
|
for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) {
|
||||||
|
if (edge->first_point().coincides_with(edge->last_point())) continue;
|
||||||
|
angles.push_back(Line(edge->first_point(), edge->last_point()).direction());
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove duplicates
|
||||||
|
double min_resolution = PI/180.0; // 1 degree
|
||||||
|
std::sort(angles.begin(), angles.end());
|
||||||
|
for (size_t i = 1; i < angles.size(); ++i) {
|
||||||
|
if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) {
|
||||||
|
angles.erase(angles.begin() + i);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* compare first value with last one and remove the greatest one (PI)
|
||||||
|
in case they are parallel (PI, 0) */
|
||||||
|
if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
|
||||||
|
angles.pop_back();
|
||||||
|
|
||||||
|
BridgeDirectionComparator bdcomp(this->extrusion_width);
|
||||||
|
double line_increment = this->extrusion_width;
|
||||||
|
bool have_coverage = false;
|
||||||
|
for (std::vector<double>::const_iterator angle = angles.begin(); angle != angles.end(); ++angle) {
|
||||||
|
Polygons my_clip_area = clip_area;
|
||||||
|
ExPolygons my_anchors = this->_anchors;
|
||||||
|
|
||||||
|
// rotate everything - the center point doesn't matter
|
||||||
|
for (Polygons::iterator it = my_clip_area.begin(); it != my_clip_area.end(); ++it)
|
||||||
|
it->rotate(-*angle, Point(0,0));
|
||||||
|
for (ExPolygons::iterator it = my_anchors.begin(); it != my_anchors.end(); ++it)
|
||||||
|
it->rotate(-*angle, Point(0,0));
|
||||||
|
|
||||||
|
// generate lines in this direction
|
||||||
|
BoundingBox bb;
|
||||||
|
for (ExPolygons::const_iterator it = my_anchors.begin(); it != my_anchors.end(); ++it)
|
||||||
|
bb.merge((Points)*it);
|
||||||
|
|
||||||
|
Lines lines;
|
||||||
|
for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment)
|
||||||
|
lines.push_back(Line(Point(bb.min.x, y), Point(bb.max.x, y)));
|
||||||
|
|
||||||
|
Lines clipped_lines;
|
||||||
|
intersection(lines, my_clip_area, clipped_lines);
|
||||||
|
|
||||||
|
// remove any line not having both endpoints within anchors
|
||||||
|
for (size_t i = 0; i < clipped_lines.size(); ++i) {
|
||||||
|
Line &line = clipped_lines[i];
|
||||||
|
if (!Slic3r::Geometry::contains_point(my_anchors, line.a)
|
||||||
|
|| !Slic3r::Geometry::contains_point(my_anchors, line.b)) {
|
||||||
|
clipped_lines.erase(clipped_lines.begin() + i);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> lengths;
|
||||||
|
double total_length = 0;
|
||||||
|
for (Lines::const_iterator line = clipped_lines.begin(); line != clipped_lines.end(); ++line) {
|
||||||
|
double len = line->length();
|
||||||
|
lengths.push_back(len);
|
||||||
|
total_length += len;
|
||||||
|
}
|
||||||
|
if (total_length) have_coverage = true;
|
||||||
|
|
||||||
|
// sum length of bridged lines
|
||||||
|
bdcomp.dir_coverage[*angle] = total_length;
|
||||||
|
|
||||||
|
/* The following produces more correct results in some cases and more broken in others.
|
||||||
|
TODO: investigate, as it looks more reliable than line clipping. */
|
||||||
|
// $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0;
|
||||||
|
|
||||||
|
// max length of bridged lines
|
||||||
|
bdcomp.dir_avg_length[*angle] = !lengths.empty()
|
||||||
|
? *std::max_element(lengths.begin(), lengths.end())
|
||||||
|
: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no direction produced coverage, then there's no bridge direction
|
||||||
|
if (!have_coverage) return false;
|
||||||
|
|
||||||
|
// sort directions by score
|
||||||
|
std::sort(angles.begin(), angles.end(), bdcomp);
|
||||||
|
|
||||||
|
this->angle = angles.front();
|
||||||
|
if (this->angle >= PI) this->angle -= PI;
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
printf(" Optimal infill angle is %d degrees\n", (int)Slic3r::Geometry::rad2deg(this->angle));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BridgeDetector::coverage(Polygons* coverage) const
|
||||||
|
{
|
||||||
|
if (this->angle == -1) return;
|
||||||
|
return this->coverage(angle, coverage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BridgeDetector::coverage(double angle, Polygons* coverage) const
|
||||||
|
{
|
||||||
|
// Clone our expolygon and rotate it so that we work with vertical lines.
|
||||||
|
ExPolygon expolygon = this->expolygon;
|
||||||
|
expolygon.rotate(PI/2.0 - angle, Point(0,0));
|
||||||
|
|
||||||
|
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
|
||||||
|
we'll use this one to generate our trapezoids and be sure that their vertices
|
||||||
|
are inside the anchors and not on their contours leading to false negatives. */
|
||||||
|
ExPolygons grown;
|
||||||
|
offset_ex(expolygon, grown, this->extrusion_width/2.0);
|
||||||
|
|
||||||
|
// Compute trapezoids according to a vertical orientation
|
||||||
|
Polygons trapezoids;
|
||||||
|
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
|
||||||
|
it->get_trapezoids2(&trapezoids, PI/2.0);
|
||||||
|
|
||||||
|
// get anchors, convert them to Polygons and rotate them too
|
||||||
|
Polygons anchors;
|
||||||
|
for (ExPolygons::const_iterator anchor = this->_anchors.begin(); anchor != this->_anchors.end(); ++anchor) {
|
||||||
|
Polygons pp = *anchor;
|
||||||
|
for (Polygons::iterator p = pp.begin(); p != pp.end(); ++p)
|
||||||
|
p->rotate(PI/2.0 - angle, Point(0,0));
|
||||||
|
anchors.insert(anchors.end(), pp.begin(), pp.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
Polygons covered;
|
||||||
|
for (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
|
||||||
|
Lines lines = trapezoid->lines();
|
||||||
|
Lines supported;
|
||||||
|
intersection(lines, anchors, supported);
|
||||||
|
|
||||||
|
// not nice, we need a more robust non-numeric check
|
||||||
|
for (size_t i = 0; i < supported.size(); ++i) {
|
||||||
|
if (supported[i].length() < this->extrusion_width) {
|
||||||
|
supported.erase(supported.begin() + i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supported.size() >= 2) covered.push_back(*trapezoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge trapezoids and rotate them back
|
||||||
|
Polygons _coverage;
|
||||||
|
union_(covered, _coverage);
|
||||||
|
for (Polygons::iterator p = _coverage.begin(); p != _coverage.end(); ++p)
|
||||||
|
p->rotate(-(PI/2.0 - angle), Point(0,0));
|
||||||
|
|
||||||
|
// intersect trapezoids with actual bridge area to remove extra margins
|
||||||
|
// and append it to result
|
||||||
|
intersection(_coverage, this->expolygon, *coverage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (0) {
|
||||||
|
my @lines = map @{$_->lines}, @$trapezoids;
|
||||||
|
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
|
||||||
|
|
||||||
|
require "Slic3r/SVG.pm";
|
||||||
|
Slic3r::SVG::output(
|
||||||
|
"coverage_" . rad2deg($angle) . ".svg",
|
||||||
|
expolygons => [$self->expolygon],
|
||||||
|
green_expolygons => $self->_anchors,
|
||||||
|
red_expolygons => $coverage,
|
||||||
|
lines => \@lines,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This method returns the bridge edges (as polylines) that are not supported
|
||||||
|
but would allow the entire bridge area to be bridged with detected angle
|
||||||
|
if supported too */
|
||||||
|
void
|
||||||
|
BridgeDetector::unsupported_edges(Polylines* unsupported) const
|
||||||
|
{
|
||||||
|
if (this->angle == -1) return;
|
||||||
|
return this->unsupported_edges(this->angle, unsupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
|
||||||
|
{
|
||||||
|
// get bridge edges (both contour and holes)
|
||||||
|
Polylines bridge_edges;
|
||||||
|
{
|
||||||
|
Polygons pp = this->expolygon;
|
||||||
|
bridge_edges.insert(bridge_edges.end(), pp.begin(), pp.end()); // this uses split_at_first_point()
|
||||||
|
}
|
||||||
|
|
||||||
|
// get unsupported edges
|
||||||
|
Polygons grown_lower;
|
||||||
|
offset(this->lower_slices, grown_lower, +this->extrusion_width);
|
||||||
|
Polylines _unsupported;
|
||||||
|
diff(bridge_edges, grown_lower, _unsupported);
|
||||||
|
|
||||||
|
/* Split into individual segments and filter out edges parallel to the bridging angle
|
||||||
|
TODO: angle tolerance should probably be based on segment length and flow width,
|
||||||
|
so that we build supports whenever there's a chance that at least one or two bridge
|
||||||
|
extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
|
||||||
|
direction might still benefit from anchors if long enough) */
|
||||||
|
double angle_tolerance = PI / 180.0 * 5.0;
|
||||||
|
for (Polylines::const_iterator polyline = _unsupported.begin(); polyline != _unsupported.end(); ++polyline) {
|
||||||
|
Lines lines = polyline->lines();
|
||||||
|
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
|
||||||
|
if (!Slic3r::Geometry::directions_parallel(line->direction(), angle))
|
||||||
|
unsupported->push_back(*line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (0) {
|
||||||
|
require "Slic3r/SVG.pm";
|
||||||
|
Slic3r::SVG::output(
|
||||||
|
"unsupported_" . rad2deg($angle) . ".svg",
|
||||||
|
expolygons => [$self->expolygon],
|
||||||
|
green_expolygons => $self->_anchors,
|
||||||
|
red_expolygons => union_ex($grown_lower),
|
||||||
|
no_arrows => 1,
|
||||||
|
polylines => \@bridge_edges,
|
||||||
|
red_polylines => $unsupported,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SLIC3RXS
|
||||||
|
REGISTER_CLASS(BridgeDetector, "BridgeDetector");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
33
xs/src/libslic3r/BridgeDetector.hpp
Normal file
33
xs/src/libslic3r/BridgeDetector.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef slic3r_BridgeDetector_hpp_
|
||||||
|
#define slic3r_BridgeDetector_hpp_
|
||||||
|
|
||||||
|
#include <myinit.h>
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
|
#include "ExPolygonCollection.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class BridgeDetector {
|
||||||
|
public:
|
||||||
|
ExPolygon expolygon;
|
||||||
|
ExPolygonCollection lower_slices;
|
||||||
|
double extrusion_width; // scaled
|
||||||
|
double resolution;
|
||||||
|
double angle;
|
||||||
|
|
||||||
|
BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
|
||||||
|
bool detect_angle();
|
||||||
|
void coverage(Polygons* coverage) const;
|
||||||
|
void coverage(double angle, Polygons* coverage) const;
|
||||||
|
void unsupported_edges(Polylines* unsupported) const;
|
||||||
|
void unsupported_edges(double angle, Polylines* unsupported) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Polylines _edges; // representing the supporting edges
|
||||||
|
ExPolygons _anchors;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -349,6 +349,23 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
|||||||
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
ClipperPaths_to_Slic3rMultiPoints(output, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject,
|
||||||
|
const Slic3r::Polygons &clip, Slic3r::Lines &retval, bool safety_offset_)
|
||||||
|
{
|
||||||
|
// convert Lines to Polylines
|
||||||
|
Slic3r::Polylines polylines;
|
||||||
|
polylines.reserve(subject.size());
|
||||||
|
for (Slic3r::Lines::const_iterator line = subject.begin(); line != subject.end(); ++line)
|
||||||
|
polylines.push_back(*line);
|
||||||
|
|
||||||
|
// perform operation
|
||||||
|
_clipper(clipType, polylines, clip, polylines, safety_offset_);
|
||||||
|
|
||||||
|
// convert Polylines to Lines
|
||||||
|
for (Slic3r::Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
|
||||||
|
retval.push_back(*polyline);
|
||||||
|
}
|
||||||
|
|
||||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||||
const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_)
|
const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_)
|
||||||
{
|
{
|
||||||
@ -406,6 +423,7 @@ template void diff<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons
|
|||||||
template void diff<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
template void diff<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
||||||
template void diff<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
template void diff<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||||
template void diff<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
template void diff<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||||
|
template void diff<Slic3r::Lines, Slic3r::Lines>(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines &retval, bool safety_offset_);
|
||||||
|
|
||||||
template <class SubjectType, class ResultType>
|
template <class SubjectType, class ResultType>
|
||||||
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
|
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
|
||||||
@ -416,6 +434,7 @@ template void intersection<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::P
|
|||||||
template void intersection<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
template void intersection<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
|
||||||
template void intersection<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
template void intersection<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||||
template void intersection<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
template void intersection<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
|
||||||
|
template void intersection<Slic3r::Lines, Slic3r::Lines>(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines &retval, bool safety_offset_);
|
||||||
|
|
||||||
template <class SubjectType>
|
template <class SubjectType>
|
||||||
bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_)
|
bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_)
|
||||||
@ -426,6 +445,7 @@ bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool s
|
|||||||
}
|
}
|
||||||
template bool intersects<Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_);
|
template bool intersects<Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_);
|
||||||
template bool intersects<Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_);
|
template bool intersects<Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_);
|
||||||
|
template bool intersects<Slic3r::Lines>(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_);
|
||||||
|
|
||||||
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
|
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
|
||||||
bool safety_offset_)
|
bool safety_offset_)
|
||||||
|
@ -77,6 +77,8 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
|||||||
const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
|
const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
|
||||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
||||||
const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
|
const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
|
||||||
|
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject,
|
||||||
|
const Slic3r::Polygons &clip, Slic3r::Lines &retval);
|
||||||
|
|
||||||
template <class SubjectType, class ResultType>
|
template <class SubjectType, class ResultType>
|
||||||
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false);
|
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
|
#include "ExPolygon.hpp"
|
||||||
#include "Line.hpp"
|
#include "Line.hpp"
|
||||||
#include "PolylineCollection.hpp"
|
#include "PolylineCollection.hpp"
|
||||||
#include "clipper.hpp"
|
#include "clipper.hpp"
|
||||||
@ -111,6 +112,37 @@ directions_parallel(double angle1, double angle2, double max_diff)
|
|||||||
return diff < max_diff || fabs(diff - PI) < max_diff;
|
return diff < max_diff || fabs(diff - PI) < max_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool
|
||||||
|
contains_point(const std::vector<T> &vector, const Point &point)
|
||||||
|
{
|
||||||
|
for (typename std::vector<T>::const_iterator it = vector.begin(); it != vector.end(); ++it) {
|
||||||
|
if (it->contains_point(point)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
template bool contains_point(const ExPolygons &vector, const Point &point);
|
||||||
|
|
||||||
|
double
|
||||||
|
rad2deg(double angle)
|
||||||
|
{
|
||||||
|
return angle / PI * 180.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
rad2deg_dir(double angle)
|
||||||
|
{
|
||||||
|
angle = (angle < PI) ? (-angle + PI/2.0) : (angle + PI/2.0);
|
||||||
|
if (angle < 0) angle += PI;
|
||||||
|
return rad2deg(angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
deg2rad(double angle)
|
||||||
|
{
|
||||||
|
return PI * angle / 180.0;
|
||||||
|
}
|
||||||
|
|
||||||
Line
|
Line
|
||||||
MedialAxis::edge_to_line(const VD::edge_type &edge) const
|
MedialAxis::edge_to_line(const VD::edge_type &edge) const
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,10 @@ void chained_path(const Points &points, std::vector<Points::size_type> &retval,
|
|||||||
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
|
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
|
||||||
template<class T> void chained_path_items(Points &points, T &items, T &retval);
|
template<class T> void chained_path_items(Points &points, T &items, T &retval);
|
||||||
bool directions_parallel(double angle1, double angle2, double max_diff = 0);
|
bool directions_parallel(double angle1, double angle2, double max_diff = 0);
|
||||||
|
template<class T> bool contains_point(const std::vector<T> &vector, const Point &point);
|
||||||
|
double rad2deg(double angle);
|
||||||
|
double rad2deg_dir(double angle);
|
||||||
|
double deg2rad(double angle);
|
||||||
|
|
||||||
class MedialAxis {
|
class MedialAxis {
|
||||||
public:
|
public:
|
||||||
|
@ -12,11 +12,13 @@ class Line;
|
|||||||
class MultiPoint;
|
class MultiPoint;
|
||||||
class Point;
|
class Point;
|
||||||
class Pointf;
|
class Pointf;
|
||||||
|
class Pointf3;
|
||||||
typedef Point Vector;
|
typedef Point Vector;
|
||||||
typedef std::vector<Point> Points;
|
typedef std::vector<Point> Points;
|
||||||
typedef std::vector<Point*> PointPtrs;
|
typedef std::vector<Point*> PointPtrs;
|
||||||
typedef std::vector<const Point*> PointConstPtrs;
|
typedef std::vector<const Point*> PointConstPtrs;
|
||||||
typedef std::vector<Pointf> Pointfs;
|
typedef std::vector<Pointf> Pointfs;
|
||||||
|
typedef std::vector<Pointf3> Pointf3s;
|
||||||
|
|
||||||
class Point
|
class Point
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "Polyline.hpp"
|
#include "Polyline.hpp"
|
||||||
|
#include "Line.hpp"
|
||||||
#include "Polygon.hpp"
|
#include "Polygon.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -10,6 +11,12 @@ Polyline::operator Polylines() const
|
|||||||
return polylines;
|
return polylines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polyline::operator Line() const
|
||||||
|
{
|
||||||
|
if (this->points.size() > 2) CONFESS("Can't convert polyline with more than two points to a line");
|
||||||
|
return Line(this->points.front(), this->points.back());
|
||||||
|
}
|
||||||
|
|
||||||
Point
|
Point
|
||||||
Polyline::last_point() const
|
Polyline::last_point() const
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@ typedef std::vector<Polyline> Polylines;
|
|||||||
class Polyline : public MultiPoint {
|
class Polyline : public MultiPoint {
|
||||||
public:
|
public:
|
||||||
operator Polylines() const;
|
operator Polylines() const;
|
||||||
|
operator Line() const;
|
||||||
Point last_point() const;
|
Point last_point() const;
|
||||||
Point leftmost_point() const;
|
Point leftmost_point() const;
|
||||||
Lines lines() const;
|
Lines lines() const;
|
||||||
|
37
xs/xsp/BridgeDetector.xsp
Normal file
37
xs/xsp/BridgeDetector.xsp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
%module{Slic3r::XS};
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include <myinit.h>
|
||||||
|
#include "libslic3r/BridgeDetector.hpp"
|
||||||
|
%}
|
||||||
|
|
||||||
|
%name{Slic3r::BridgeDetector} class BridgeDetector {
|
||||||
|
~BridgeDetector();
|
||||||
|
|
||||||
|
bool detect_angle();
|
||||||
|
Polygons coverage()
|
||||||
|
%code{% THIS->coverage(&RETVAL); %};
|
||||||
|
Polygons coverage_by_angle(double angle)
|
||||||
|
%code{% THIS->coverage(angle, &RETVAL); %};
|
||||||
|
Polylines unsupported_edges()
|
||||||
|
%code{% THIS->unsupported_edges(&RETVAL); %};
|
||||||
|
Polylines unsupported_edges_by_angle(double angle)
|
||||||
|
%code{% THIS->unsupported_edges(angle, &RETVAL); %};
|
||||||
|
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;
|
||||||
|
long extrusion_width;
|
||||||
|
CODE:
|
||||||
|
RETVAL = new BridgeDetector(*expolygon, *lower_slices, extrusion_width);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
%}
|
||||||
|
};
|
@ -55,4 +55,28 @@ chained_path_from(points, start_from)
|
|||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
|
double
|
||||||
|
rad2deg(angle)
|
||||||
|
double angle
|
||||||
|
CODE:
|
||||||
|
RETVAL = Slic3r::Geometry::rad2deg(angle);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
double
|
||||||
|
rad2deg_dir(angle)
|
||||||
|
double angle
|
||||||
|
CODE:
|
||||||
|
RETVAL = Slic3r::Geometry::rad2deg_dir(angle);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
|
double
|
||||||
|
deg2rad(angle)
|
||||||
|
double angle
|
||||||
|
CODE:
|
||||||
|
RETVAL = Slic3r::Geometry::deg2rad(angle);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
@ -165,6 +165,10 @@ GCodeWriter* O_OBJECT_SLIC3R
|
|||||||
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
|
BridgeDetector* O_OBJECT_SLIC3R
|
||||||
|
Ref<BridgeDetector> O_OBJECT_SLIC3R_T
|
||||||
|
Clone<BridgeDetector> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
ExtrusionLoopRole T_UV
|
ExtrusionLoopRole T_UV
|
||||||
ExtrusionRole T_UV
|
ExtrusionRole T_UV
|
||||||
FlowRole T_UV
|
FlowRole T_UV
|
||||||
|
@ -87,6 +87,9 @@
|
|||||||
%typemap{GCodeWriter*};
|
%typemap{GCodeWriter*};
|
||||||
%typemap{Ref<GCodeWriter>}{simple};
|
%typemap{Ref<GCodeWriter>}{simple};
|
||||||
%typemap{Clone<GCodeWriter>}{simple};
|
%typemap{Clone<GCodeWriter>}{simple};
|
||||||
|
%typemap{BridgeDetector*};
|
||||||
|
%typemap{Ref<BridgeDetector>}{simple};
|
||||||
|
%typemap{Clone<BridgeDetector>}{simple};
|
||||||
|
|
||||||
%typemap{Surface*};
|
%typemap{Surface*};
|
||||||
%typemap{Ref<Surface>}{simple};
|
%typemap{Ref<Surface>}{simple};
|
||||||
|
Loading…
Reference in New Issue
Block a user