Slic3r should now be able to detect optimal bridging direction for any kind of bridge. #58
This commit is contained in:
parent
5375f5fef4
commit
792960aae1
1
MANIFEST
1
MANIFEST
@ -41,6 +41,7 @@ lib/Slic3r/TriangleMesh/IntersectionLine.pm
|
||||
MANIFEST This list of files
|
||||
README.markdown
|
||||
slic3r.pl
|
||||
t/angles.t
|
||||
t/arcs.t
|
||||
t/clean_polylines.t
|
||||
t/clipper.t
|
||||
|
@ -53,7 +53,7 @@ sub make_fill {
|
||||
{
|
||||
my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layer->fill_surfaces};
|
||||
foreach my $group (Slic3r::Surface->group({merge_solid => 1}, @{$layer->fill_surfaces})) {
|
||||
my $union = union_ex([ map $_->p, @$group ]);
|
||||
my $union = union_ex([ map $_->p, @$group ], undef, 1);
|
||||
|
||||
# subtract surfaces having a defined bridge_angle from any other
|
||||
if (@surfaces_with_bridge_angle && !defined $group->[0]->bridge_angle) {
|
||||
|
@ -14,10 +14,11 @@ our @EXPORT_OK = qw(
|
||||
rotate_points move_points remove_coinciding_points clip_segment_polygon
|
||||
sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility
|
||||
line_intersection bounding_box bounding_box_intersect same_point
|
||||
longest_segment angle3points three_points_aligned
|
||||
longest_segment angle3points three_points_aligned line_direction
|
||||
polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
|
||||
polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges
|
||||
shortest_path collinear scale unscale merge_collinear_lines
|
||||
rad2deg_dir
|
||||
);
|
||||
|
||||
use Slic3r::Geometry::DouglasPeucker qw(Douglas_Peucker);
|
||||
@ -52,6 +53,14 @@ sub line_atan {
|
||||
return atan2($line->[B][Y] - $line->[A][Y], $line->[B][X] - $line->[A][X]);
|
||||
}
|
||||
|
||||
sub line_direction {
|
||||
my ($line) = @_;
|
||||
my $atan2 = line_atan($line);
|
||||
return ($atan2 == PI) ? 0
|
||||
: ($atan2 < 0) ? ($atan2 + PI)
|
||||
: $atan2;
|
||||
}
|
||||
|
||||
sub lines_parallel {
|
||||
my ($line1, $line2) = @_;
|
||||
|
||||
@ -311,6 +320,13 @@ sub rad2deg {
|
||||
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 {
|
||||
my ($radians, $center, @points) = @_;
|
||||
$center ||= [0,0];
|
||||
|
@ -9,6 +9,7 @@ our @EXPORT_OK = qw(explode_expolygon explode_expolygons safety_offset offset
|
||||
is_counter_clockwise);
|
||||
|
||||
use Math::Clipper 1.02 ':all';
|
||||
use Slic3r::Geometry qw(scale);
|
||||
our $clipper = Math::Clipper->new;
|
||||
|
||||
sub explode_expolygon {
|
||||
@ -23,15 +24,15 @@ sub explode_expolygons {
|
||||
|
||||
sub safety_offset {
|
||||
my ($polygons) = @_;
|
||||
return Math::Clipper::offset($polygons, 100, 100, JT_MITER, 2);
|
||||
return Math::Clipper::offset($polygons, scale 1e-05, 100, JT_MITER, 2);
|
||||
}
|
||||
|
||||
sub diff_ex {
|
||||
my ($subject, $clip) = @_;
|
||||
my ($subject, $clip, $safety_offset) = @_;
|
||||
|
||||
$clipper->clear;
|
||||
$clipper->add_subject_polygons($subject);
|
||||
$clipper->add_clip_polygons($clip);
|
||||
$clipper->add_clip_polygons($safety_offset ? safety_offset($clip) : $clip);
|
||||
return [
|
||||
map Slic3r::ExPolygon->new($_),
|
||||
@{ $clipper->ex_execute(CT_DIFFERENCE, PFT_NONZERO, PFT_NONZERO) },
|
||||
@ -43,10 +44,10 @@ sub diff {
|
||||
}
|
||||
|
||||
sub union_ex {
|
||||
my ($polygons, $jointype) = @_;
|
||||
my ($polygons, $jointype, $safety_offset) = @_;
|
||||
$jointype = PFT_NONZERO unless defined $jointype;
|
||||
$clipper->clear;
|
||||
$clipper->add_subject_polygons($polygons);
|
||||
$clipper->add_subject_polygons($safety_offset ? safety_offset($polygons) : $polygons);
|
||||
return [
|
||||
map Slic3r::ExPolygon->new($_),
|
||||
@{ $clipper->ex_execute(CT_UNION, $jointype, $jointype) },
|
||||
@ -54,11 +55,11 @@ sub union_ex {
|
||||
}
|
||||
|
||||
sub intersection_ex {
|
||||
my ($subject, $clip, $jointype) = @_;
|
||||
my ($subject, $clip, $jointype, $safety_offset) = @_;
|
||||
$jointype = PFT_NONZERO unless defined $jointype;
|
||||
$clipper->clear;
|
||||
$clipper->add_subject_polygons($subject);
|
||||
$clipper->add_clip_polygons($clip);
|
||||
$clipper->add_clip_polygons($safety_offset ? safety_offset($clip) : $clip);
|
||||
return [
|
||||
map Slic3r::ExPolygon->new($_),
|
||||
@{ $clipper->ex_execute(CT_INTERSECTION, $jointype, $jointype) },
|
||||
|
@ -2,7 +2,7 @@ package Slic3r::Layer;
|
||||
use Moo;
|
||||
|
||||
use Math::Clipper ':all';
|
||||
use Slic3r::Geometry qw(scale collinear X Y A B PI);
|
||||
use Slic3r::Geometry qw(scale collinear X Y A B PI rad2deg_dir);
|
||||
use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex is_counter_clockwise);
|
||||
use XXX;
|
||||
|
||||
@ -224,7 +224,7 @@ sub process_bridges {
|
||||
# offset the contour and intersect it with the internal surfaces to discover
|
||||
# which of them has contact with our bridge
|
||||
my @supporting_surfaces = ();
|
||||
my ($contour_offset) = $expolygon->contour->offset($Slic3r::flow_width / $Slic3r::resolution);
|
||||
my ($contour_offset) = $expolygon->contour->offset(scale $Slic3r::flow_width * sqrt(2));
|
||||
foreach my $internal_surface (@internal_surfaces) {
|
||||
my $intersection = intersection_ex([$contour_offset], [$internal_surface->contour->p]);
|
||||
if (@$intersection) {
|
||||
@ -236,7 +236,7 @@ sub process_bridges {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(undef, "bridge.svg",
|
||||
green_polygons => [ map $_->p, @supporting_surfaces ],
|
||||
red_polygons => [ @$expolygon ],
|
||||
#red_polygons => [ @$expolygon ],
|
||||
);
|
||||
}
|
||||
|
||||
@ -257,6 +257,7 @@ sub process_bridges {
|
||||
$bridge_over_hole = 1;
|
||||
} else {
|
||||
foreach my $edge (@surface_edges) {
|
||||
next unless @{$edge->points} >= 4;
|
||||
shift @{$edge->points};
|
||||
pop @{$edge->points};
|
||||
}
|
||||
@ -272,15 +273,21 @@ sub process_bridges {
|
||||
Slic3r::SVG::output(undef, "bridge.svg",
|
||||
polylines => [ map $_->p, @edges ],
|
||||
);
|
||||
exit if $self->id == 30;
|
||||
}
|
||||
|
||||
if (@edges == 2) {
|
||||
my @chords = map Slic3r::Line->new($_->points->[0], $_->points->[-1]), @edges;
|
||||
my @midpoints = map $_->midpoint, @chords;
|
||||
$bridge_angle = -Slic3r::Geometry::rad2deg(Slic3r::Geometry::line_atan(\@midpoints) + PI/2);
|
||||
Slic3r::debugf "Optimal infill angle of bridge on layer %d is %d degrees\n", $self->id, $bridge_angle;
|
||||
{
|
||||
my $weighted_sum = 0;
|
||||
my $total_length = 0;
|
||||
foreach my $line (map $_->lines, @edges) {
|
||||
my $len = $line->length;
|
||||
$weighted_sum += $len * $line->direction;
|
||||
$total_length += $len;
|
||||
}
|
||||
$bridge_angle = rad2deg_dir(($weighted_sum / $total_length) + PI/2);
|
||||
}
|
||||
|
||||
Slic3r::debugf "Optimal infill angle of bridge on layer %d is %d degrees\n",
|
||||
$self->id, $bridge_angle if defined $bridge_angle;
|
||||
}
|
||||
|
||||
# now, extend our bridge by taking a portion of supporting surfaces
|
||||
|
@ -85,6 +85,11 @@ sub atan {
|
||||
return Slic3r::Geometry::line_atan($self);
|
||||
}
|
||||
|
||||
sub direction {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::line_direction($self);
|
||||
}
|
||||
|
||||
sub intersection {
|
||||
my $self = shift;
|
||||
my ($line, $require_crossing) = @_;
|
||||
|
@ -3,7 +3,7 @@ use Moo;
|
||||
|
||||
use Math::Clipper qw();
|
||||
use Slic3r::Geometry qw(A B polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
|
||||
polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges move_points);
|
||||
polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges move_points same_point);
|
||||
use Sub::Quote;
|
||||
use XXX;
|
||||
|
||||
@ -156,9 +156,14 @@ sub clip_with_expolygon {
|
||||
push @polylines, $current_polyline;
|
||||
}
|
||||
|
||||
if (@polylines > 1 && scalar(@{$polylines[-1]}) == 2 && $polylines[-1][-1] eq $polylines[0][0]) {
|
||||
if (@polylines > 1 && same_point($polylines[-1][-1], $polylines[0][0])) {
|
||||
if (scalar(@{$polylines[-1]}) == 2) {
|
||||
unshift @{$polylines[0]}, $polylines[-1][0];
|
||||
pop @polylines;
|
||||
} else {
|
||||
push @{$polylines[-1]}, $polylines[0][-1];
|
||||
shift @polylines;
|
||||
}
|
||||
}
|
||||
|
||||
return map Slic3r::Polyline->cast($_), @polylines;
|
||||
|
@ -177,6 +177,7 @@ sub detect_surfaces_type {
|
||||
my $expolygons = diff_ex(
|
||||
[ map { ref $_ eq 'ARRAY' ? $_ : ref $_ eq 'Slic3r::ExPolygon' ? @$_ : $_->p } @$subject_surfaces ],
|
||||
[ map { ref $_ eq 'ARRAY' ? $_ : ref $_ eq 'Slic3r::ExPolygon' ? @$_ : $_->p } @$clip_surfaces ],
|
||||
1,
|
||||
);
|
||||
return grep $_->contour->is_printable,
|
||||
map Slic3r::Surface->cast_from_expolygon($_, surface_type => $result_type),
|
||||
@ -206,7 +207,6 @@ sub detect_surfaces_type {
|
||||
# of current layer and upper one)
|
||||
if ($upper_layer) {
|
||||
@top = $surface_difference->($layer->surfaces, $upper_layer->surfaces, 'top');
|
||||
|
||||
} else {
|
||||
# if no upper layer, all surfaces of this one are solid
|
||||
@top = @{$layer->surfaces};
|
||||
@ -217,22 +217,6 @@ sub detect_surfaces_type {
|
||||
# of current layer and lower one)
|
||||
if ($lower_layer) {
|
||||
@bottom = $surface_difference->($layer->surfaces, $lower_layer->surfaces, 'bottom');
|
||||
|
||||
$_->contour->merge_continuous_lines for @bottom;
|
||||
|
||||
# merge_continuous_lines could return polylines with less than 3 points (thus invalid)
|
||||
# actually, this shouldn't happen so it deserves further investigation
|
||||
@bottom = grep $_->contour->is_valid, @bottom;
|
||||
|
||||
foreach my $surface (@bottom) {
|
||||
$surface->contour->remove_acute_vertices;
|
||||
|
||||
# okay, this is an Ugly Hack(tm) to avoid floating point math problems
|
||||
# with diagonal bridges. will find a nicer solution, promised.
|
||||
my $offset = safety_offset([$surface->contour->p]);
|
||||
@{$surface->contour->points} = map Slic3r::Point->new($_), @{ $offset->[0] };
|
||||
}
|
||||
|
||||
} else {
|
||||
# if no lower layer, all surfaces of this one are solid
|
||||
@bottom = @{$layer->surfaces};
|
||||
@ -391,7 +375,6 @@ sub infill_every_layers {
|
||||
[ map $_->p, grep $_->surface_type eq 'internal', @{$layer->fill_surfaces} ],
|
||||
);
|
||||
next if !@$intersection;
|
||||
my $intersection_offsetted = safety_offset([ map @$_, @$intersection ]);
|
||||
|
||||
# new fill surfaces of the current layer are:
|
||||
# - any non-internal surface
|
||||
@ -414,7 +397,8 @@ sub infill_every_layers {
|
||||
map $_->p, grep $_->surface_type eq 'internal' && $_->depth_layers == $depth,
|
||||
@{$layer->fill_surfaces},
|
||||
],
|
||||
$intersection_offsetted,
|
||||
$intersection,
|
||||
1,
|
||||
)};
|
||||
}
|
||||
@{$layer->fill_surfaces} = @new_surfaces;
|
||||
@ -435,7 +419,8 @@ sub infill_every_layers {
|
||||
map $_->p, grep $_->surface_type eq 'internal' && $_->depth_layers == $depth,
|
||||
@{$lower_layer->fill_surfaces},
|
||||
],
|
||||
$intersection_offsetted,
|
||||
$intersection,
|
||||
1,
|
||||
)};
|
||||
}
|
||||
@{$lower_layer->fill_surfaces} = @new_surfaces;
|
||||
|
55
t/angles.t
Normal file
55
t/angles.t
Normal file
@ -0,0 +1,55 @@
|
||||
use Test::More;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
plan tests => 23;
|
||||
|
||||
BEGIN {
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
}
|
||||
|
||||
use Slic3r;
|
||||
use Slic3r::Geometry qw(line_atan line_direction rad2deg_dir PI);
|
||||
|
||||
#==========================================================
|
||||
|
||||
{
|
||||
is line_atan([ [0, 0], [10, 0] ]), (0), 'E atan2';
|
||||
is line_atan([ [10, 0], [0, 0] ]), (PI), 'W atan2';
|
||||
is line_atan([ [0, 0], [0, 10] ]), (PI/2), 'N atan2';
|
||||
is line_atan([ [0, 10], [0, 0] ]), -(PI/2), 'S atan2';
|
||||
|
||||
is line_atan([ [10, 10], [0, 0] ]), -(PI*3/4), 'SW atan2';
|
||||
is line_atan([ [0, 0], [10, 10] ]), (PI*1/4), 'NE atan2';
|
||||
is line_atan([ [0, 10], [10, 0] ]), -(PI*1/4), 'SE atan2';
|
||||
is line_atan([ [10, 0], [0, 10] ]), (PI*3/4), 'NW atan2';
|
||||
}
|
||||
|
||||
#==========================================================
|
||||
|
||||
{
|
||||
is line_direction([ [0, 0], [10, 0] ]), (0), 'E direction';
|
||||
is line_direction([ [10, 0], [0, 0] ]), (0), 'W direction';
|
||||
is line_direction([ [0, 0], [0, 10] ]), (PI/2), 'N direction';
|
||||
is line_direction([ [0, 10], [0, 0] ]), (PI/2), 'S direction';
|
||||
|
||||
is line_direction([ [10, 10], [0, 0] ]), (PI*1/4), 'SW direction';
|
||||
is line_direction([ [0, 0], [10, 10] ]), (PI*1/4), 'NE direction';
|
||||
is line_direction([ [0, 10], [10, 0] ]), (PI*3/4), 'SE direction';
|
||||
is line_direction([ [10, 0], [0, 10] ]), (PI*3/4), 'NW direction';
|
||||
}
|
||||
|
||||
#==========================================================
|
||||
|
||||
{
|
||||
is rad2deg_dir(0), 90, 'E (degrees)';
|
||||
is rad2deg_dir(PI), 270, 'W (degrees)';
|
||||
is rad2deg_dir(PI/2), 0, 'N (degrees)';
|
||||
is rad2deg_dir(-(PI/2)), 180, 'S (degrees)';
|
||||
is rad2deg_dir(PI*1/4), 45, 'NE (degrees)';
|
||||
is rad2deg_dir(PI*3/4), 135, 'NW (degrees)';
|
||||
is rad2deg_dir(PI/6), 60, '30°';
|
||||
}
|
||||
|
||||
#==========================================================
|
Loading…
Reference in New Issue
Block a user