mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-21 23:14:27 +08:00
Merge pull request #3610 from alexrj/new-rectilinear
New Rectilinear implementation
This commit is contained in:
commit
43c62d468c
@ -50,7 +50,6 @@ add_library(libslic3r STATIC
|
|||||||
${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp
|
${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp
|
||||||
${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp
|
${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp
|
||||||
${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp
|
${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp
|
||||||
${LIBDIR}/libslic3r/Fill/FillRectilinear2.cpp
|
|
||||||
${LIBDIR}/libslic3r/Flow.cpp
|
${LIBDIR}/libslic3r/Flow.cpp
|
||||||
${LIBDIR}/libslic3r/GCode.cpp
|
${LIBDIR}/libslic3r/GCode.cpp
|
||||||
${LIBDIR}/libslic3r/GCodeSender.cpp
|
${LIBDIR}/libslic3r/GCodeSender.cpp
|
||||||
|
69
t/fill.t
69
t/fill.t
@ -2,7 +2,7 @@ use Test::More;
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
plan tests => 43;
|
plan tests => 92;
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
use FindBin;
|
use FindBin;
|
||||||
@ -11,8 +11,8 @@ BEGIN {
|
|||||||
|
|
||||||
use List::Util qw(first sum);
|
use List::Util qw(first sum);
|
||||||
use Slic3r;
|
use Slic3r;
|
||||||
use Slic3r::Geometry qw(PI X Y scale unscale convex_hull);
|
use Slic3r::Geometry qw(PI X Y scaled_epsilon scale unscale convex_hull);
|
||||||
use Slic3r::Geometry::Clipper qw(union diff diff_ex offset offset2_ex);
|
use Slic3r::Geometry::Clipper qw(union diff diff_ex offset offset2_ex diff_pl);
|
||||||
use Slic3r::Surface qw(:types);
|
use Slic3r::Surface qw(:types);
|
||||||
use Slic3r::Test;
|
use Slic3r::Test;
|
||||||
|
|
||||||
@ -26,6 +26,67 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
|
|||||||
is $surface_width % $distance, 0, 'adjusted solid distance';
|
is $surface_width % $distance, 0, 'adjusted solid distance';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $filler = Slic3r::Filler->new_from_type('rectilinear');
|
||||||
|
$filler->set_angle(-(PI)/2);
|
||||||
|
$filler->set_spacing(5);
|
||||||
|
$filler->set_dont_adjust(1);
|
||||||
|
$filler->set_endpoints_overlap(0);
|
||||||
|
|
||||||
|
my $test = sub {
|
||||||
|
my ($expolygon) = @_;
|
||||||
|
my $surface = Slic3r::Surface->new(
|
||||||
|
surface_type => S_TYPE_TOP,
|
||||||
|
expolygon => $expolygon,
|
||||||
|
);
|
||||||
|
return $filler->fill_surface($surface);
|
||||||
|
};
|
||||||
|
|
||||||
|
# square
|
||||||
|
$filler->set_density($filler->spacing / 50);
|
||||||
|
for my $i (0..3) {
|
||||||
|
# check that it works regardless of the points order
|
||||||
|
my @points = ([0,0], [100,0], [100,100], [0,100]);
|
||||||
|
@points = (@points[$i..$#points], @points[0..($i-1)]);
|
||||||
|
my $paths = $test->(my $e = Slic3r::ExPolygon->new([ scale_points @points ]));
|
||||||
|
|
||||||
|
is(scalar @$paths, 1, 'one continuous path') or done_testing, exit;
|
||||||
|
ok abs($paths->[0]->length - scale(3*100 + 2*50)) - scaled_epsilon, 'path has expected length';
|
||||||
|
}
|
||||||
|
|
||||||
|
# diamond with endpoints on grid
|
||||||
|
{
|
||||||
|
my $paths = $test->(my $e = Slic3r::ExPolygon->new([ scale_points [0,0], [100,0], [150,50], [100,100], [0,100], [-50,50] ]));
|
||||||
|
is(scalar @$paths, 1, 'one continuous path') or done_testing, exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
# square with hole
|
||||||
|
for my $angle (-(PI/2), -(PI/4), -(PI), PI/2, PI) {
|
||||||
|
for my $spacing (25, 5, 7.5, 8.5) {
|
||||||
|
$filler->set_density($filler->spacing / $spacing);
|
||||||
|
$filler->set_angle($angle);
|
||||||
|
my $paths = $test->(my $e = Slic3r::ExPolygon->new(
|
||||||
|
[ scale_points [0,0], [100,0], [100,100], [0,100] ],
|
||||||
|
[ scale_points reverse [25,25], [75,25], [75,75], [25,75] ],
|
||||||
|
));
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
require "Slic3r/SVG.pm";
|
||||||
|
Slic3r::SVG::output(
|
||||||
|
"fill.svg",
|
||||||
|
no_arrows => 1,
|
||||||
|
expolygons => [$e],
|
||||||
|
polylines => $paths,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(@$paths >= 2 && @$paths <= 3, '2 or 3 continuous paths') or done_testing, exit;
|
||||||
|
ok(!@{diff_pl($paths->arrayref, offset(\@$e, +scaled_epsilon*10))},
|
||||||
|
'paths don\'t cross hole') or done_testing, exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
my $expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]);
|
my $expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]);
|
||||||
my $filler = Slic3r::Filler->new_from_type('rectilinear');
|
my $filler = Slic3r::Filler->new_from_type('rectilinear');
|
||||||
@ -41,6 +102,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
|
|||||||
nozzle_diameter => 0.50,
|
nozzle_diameter => 0.50,
|
||||||
);
|
);
|
||||||
$filler->set_spacing($flow->spacing);
|
$filler->set_spacing($flow->spacing);
|
||||||
|
$filler->set_density(1);
|
||||||
foreach my $angle (0, 45) {
|
foreach my $angle (0, 45) {
|
||||||
$surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]);
|
$surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]);
|
||||||
my $paths = $filler->fill_surface($surface, layer_height => 0.4, density => 0.4);
|
my $paths = $filler->fill_surface($surface, layer_height => 0.4, density => 0.4);
|
||||||
@ -55,6 +117,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
|
|||||||
my $filler = Slic3r::Filler->new_from_type('rectilinear');
|
my $filler = Slic3r::Filler->new_from_type('rectilinear');
|
||||||
$filler->set_bounding_box($expolygon->bounding_box);
|
$filler->set_bounding_box($expolygon->bounding_box);
|
||||||
$filler->set_angle($angle // 0);
|
$filler->set_angle($angle // 0);
|
||||||
|
$filler->set_dont_adjust(0);
|
||||||
my $surface = Slic3r::Surface->new(
|
my $surface = Slic3r::Surface->new(
|
||||||
surface_type => S_TYPE_BOTTOM,
|
surface_type => S_TYPE_BOTTOM,
|
||||||
expolygon => $expolygon,
|
expolygon => $expolygon,
|
||||||
|
@ -40,8 +40,6 @@ src/libslic3r/Fill/FillPlanePath.cpp
|
|||||||
src/libslic3r/Fill/FillPlanePath.hpp
|
src/libslic3r/Fill/FillPlanePath.hpp
|
||||||
src/libslic3r/Fill/FillRectilinear.cpp
|
src/libslic3r/Fill/FillRectilinear.cpp
|
||||||
src/libslic3r/Fill/FillRectilinear.hpp
|
src/libslic3r/Fill/FillRectilinear.hpp
|
||||||
src/libslic3r/Fill/FillRectilinear2.cpp
|
|
||||||
src/libslic3r/Fill/FillRectilinear2.hpp
|
|
||||||
src/libslic3r/Flow.cpp
|
src/libslic3r/Flow.cpp
|
||||||
src/libslic3r/Flow.hpp
|
src/libslic3r/Flow.hpp
|
||||||
src/libslic3r/GCode.cpp
|
src/libslic3r/GCode.cpp
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include "Fill3DHoneycomb.hpp"
|
#include "Fill3DHoneycomb.hpp"
|
||||||
#include "FillPlanePath.hpp"
|
#include "FillPlanePath.hpp"
|
||||||
#include "FillRectilinear.hpp"
|
#include "FillRectilinear.hpp"
|
||||||
#include "FillRectilinear2.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -24,12 +23,9 @@ Fill::new_from_type(const InfillPattern type)
|
|||||||
case ip3DHoneycomb: return new Fill3DHoneycomb();
|
case ip3DHoneycomb: return new Fill3DHoneycomb();
|
||||||
|
|
||||||
case ipRectilinear: return new FillRectilinear();
|
case ipRectilinear: return new FillRectilinear();
|
||||||
case ipLine: return new FillLine();
|
|
||||||
case ipGrid: return new FillGrid();
|
|
||||||
case ipAlignedRectilinear: return new FillAlignedRectilinear();
|
case ipAlignedRectilinear: return new FillAlignedRectilinear();
|
||||||
|
case ipGrid: return new FillGrid();
|
||||||
|
|
||||||
case ipRectilinear2: return new FillRectilinear2();
|
|
||||||
case ipGrid2: return new FillGrid2();
|
|
||||||
case ipTriangles: return new FillTriangles();
|
case ipTriangles: return new FillTriangles();
|
||||||
case ipStars: return new FillStars();
|
case ipStars: return new FillStars();
|
||||||
case ipCubic: return new FillCubic();
|
case ipCubic: return new FillCubic();
|
||||||
@ -80,11 +76,10 @@ Fill::adjust_solid_spacing(const coord_t width, const coord_t distance)
|
|||||||
{
|
{
|
||||||
assert(width >= 0);
|
assert(width >= 0);
|
||||||
assert(distance > 0);
|
assert(distance > 0);
|
||||||
// floor(width / distance)
|
const int number_of_intervals = floor(width / distance);
|
||||||
coord_t number_of_intervals = floor(width / distance);
|
if (number_of_intervals == 0) return distance;
|
||||||
coord_t distance_new = (number_of_intervals == 0)
|
|
||||||
? distance
|
coord_t distance_new = (width / number_of_intervals);
|
||||||
: (width / number_of_intervals);
|
|
||||||
|
|
||||||
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
|
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
|
||||||
assert(factor > 1. - 1e-5);
|
assert(factor > 1. - 1e-5);
|
||||||
@ -94,12 +89,14 @@ Fill::adjust_solid_spacing(const coord_t width, const coord_t distance)
|
|||||||
if (factor > factor_max)
|
if (factor > factor_max)
|
||||||
distance_new = floor((double)distance * factor_max + 0.5);
|
distance_new = floor((double)distance * factor_max + 0.5);
|
||||||
|
|
||||||
|
assert((distance_new * number_of_intervals) <= width);
|
||||||
|
|
||||||
return distance_new;
|
return distance_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns orientation of the infill and the reference point of the infill pattern.
|
// Returns orientation of the infill and the reference point of the infill pattern.
|
||||||
// For a normal print, the reference point is the center of a bounding box of the STL.
|
// For a normal print, the reference point is the center of a bounding box of the STL.
|
||||||
std::pair<float, Point>
|
Fill::direction_t
|
||||||
Fill::_infill_direction(const Surface &surface) const
|
Fill::_infill_direction(const Surface &surface) const
|
||||||
{
|
{
|
||||||
// set infill angle
|
// set infill angle
|
||||||
@ -133,7 +130,7 @@ Fill::_infill_direction(const Surface &surface) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
out_angle += float(M_PI/2.);
|
out_angle += float(M_PI/2.);
|
||||||
return std::pair<float, Point>(out_angle, out_shift);
|
return direction_t(out_angle, out_shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -29,6 +29,9 @@ public:
|
|||||||
// in unscaled coordinates
|
// in unscaled coordinates
|
||||||
coordf_t spacing;
|
coordf_t spacing;
|
||||||
|
|
||||||
|
// overlap over spacing for extrusion endpoints
|
||||||
|
float endpoints_overlap;
|
||||||
|
|
||||||
// in radians, ccw, 0 = East
|
// in radians, ccw, 0 = East
|
||||||
float angle;
|
float angle;
|
||||||
|
|
||||||
@ -80,6 +83,7 @@ protected:
|
|||||||
layer_id(size_t(-1)),
|
layer_id(size_t(-1)),
|
||||||
z(0.f),
|
z(0.f),
|
||||||
spacing(0.f),
|
spacing(0.f),
|
||||||
|
endpoints_overlap(0.3f),
|
||||||
angle(0),
|
angle(0),
|
||||||
link_max_length(0),
|
link_max_length(0),
|
||||||
loop_clipping(0),
|
loop_clipping(0),
|
||||||
@ -89,10 +93,12 @@ protected:
|
|||||||
complete(false)
|
complete(false)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
typedef std::pair<float, Point> direction_t;
|
||||||
|
|
||||||
// The expolygon may be modified by the method to avoid a copy.
|
// The expolygon may be modified by the method to avoid a copy.
|
||||||
virtual void _fill_surface_single(
|
virtual void _fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out) {};
|
Polylines* polylines_out) {};
|
||||||
|
|
||||||
@ -101,7 +107,7 @@ protected:
|
|||||||
return (idx % 2) == 0 ? (M_PI/2.) : 0;
|
return (idx % 2) == 0 ? (M_PI/2.) : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::pair<float, Point> _infill_direction(const Surface &surface) const;
|
direction_t _infill_direction(const Surface &surface) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -150,7 +150,7 @@ makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t gridHeight, size_
|
|||||||
void
|
void
|
||||||
Fill3DHoneycomb::_fill_surface_single(
|
Fill3DHoneycomb::_fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out)
|
Polylines* polylines_out)
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
virtual void _fill_surface_single(
|
virtual void _fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out);
|
Polylines* polylines_out);
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@ namespace Slic3r {
|
|||||||
void
|
void
|
||||||
FillConcentric::_fill_surface_single(
|
FillConcentric::_fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out)
|
Polylines* polylines_out)
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ protected:
|
|||||||
virtual Fill* clone() const { return new FillConcentric(*this); };
|
virtual Fill* clone() const { return new FillConcentric(*this); };
|
||||||
virtual void _fill_surface_single(
|
virtual void _fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out);
|
Polylines* polylines_out);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ namespace Slic3r {
|
|||||||
void
|
void
|
||||||
FillHoneycomb::_fill_surface_single(
|
FillHoneycomb::_fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out)
|
Polylines* polylines_out)
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ protected:
|
|||||||
virtual Fill* clone() const { return new FillHoneycomb(*this); };
|
virtual Fill* clone() const { return new FillHoneycomb(*this); };
|
||||||
virtual void _fill_surface_single(
|
virtual void _fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out
|
Polylines* polylines_out
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,7 @@ namespace Slic3r {
|
|||||||
|
|
||||||
void FillPlanePath::_fill_surface_single(
|
void FillPlanePath::_fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out)
|
Polylines* polylines_out)
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
virtual void _fill_surface_single(
|
virtual void _fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out);
|
Polylines* polylines_out);
|
||||||
|
|
||||||
|
@ -2,138 +2,479 @@
|
|||||||
#include "../ExPolygon.hpp"
|
#include "../ExPolygon.hpp"
|
||||||
#include "../PolylineCollection.hpp"
|
#include "../PolylineCollection.hpp"
|
||||||
#include "../Surface.hpp"
|
#include "../Surface.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "FillRectilinear.hpp"
|
#include "FillRectilinear.hpp"
|
||||||
|
|
||||||
|
//#define DEBUG_RECTILINEAR
|
||||||
|
#ifdef DEBUG_RECTILINEAR
|
||||||
|
#include "../SVG.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
void FillRectilinear::_fill_surface_single(
|
void
|
||||||
unsigned int thickness_layers,
|
FillRectilinear::_fill_single_direction(ExPolygon expolygon,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction, coord_t x_shift, Polylines* out)
|
||||||
ExPolygon &expolygon,
|
|
||||||
Polylines* polylines_out)
|
|
||||||
{
|
{
|
||||||
assert(this->density > 0.0001f && this->density <= 1.f);
|
|
||||||
|
|
||||||
// rotate polygons so that we can work with vertical lines here
|
// rotate polygons so that we can work with vertical lines here
|
||||||
expolygon.rotate(-direction.first);
|
expolygon.rotate(-direction.first);
|
||||||
|
|
||||||
this->_min_spacing = scale_(this->spacing);
|
assert(this->density > 0.0001f && this->density <= 1.f);
|
||||||
this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / this->density);
|
const coord_t min_spacing = scale_(this->spacing);
|
||||||
this->_diagonal_distance = this->_line_spacing * 2;
|
coord_t line_spacing = (double) min_spacing / this->density;
|
||||||
this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill
|
|
||||||
|
|
||||||
// We ignore this->bounding_box because it doesn't matter; we're doing align_to_grid below.
|
// We ignore this->bounding_box because it doesn't matter; we're doing align_to_grid below.
|
||||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||||
|
|
||||||
|
// Due to integer rounding, rotated polygons might not preserve verticality
|
||||||
|
// (i.e. when rotating by PI/2 two points having the same x coordinate
|
||||||
|
// they might get different y coordinates), thus the first line will be skipped.
|
||||||
|
bounding_box.offset(-1);
|
||||||
|
|
||||||
// define flow spacing according to requested density
|
// define flow spacing according to requested density
|
||||||
if (this->density > 0.9999f && !this->dont_adjust) {
|
if (this->density > 0.9999f && !this->dont_adjust) {
|
||||||
this->_line_spacing = this->adjust_solid_spacing(bounding_box.size().x, this->_line_spacing);
|
line_spacing = this->adjust_solid_spacing(bounding_box.size().x, line_spacing);
|
||||||
this->spacing = unscale(this->_line_spacing);
|
this->spacing = unscale(line_spacing);
|
||||||
} else {
|
} else {
|
||||||
// extend bounding box so that our pattern will be aligned with other layers
|
// extend bounding box so that our pattern will be aligned with other layers
|
||||||
// Transform the reference point to the rotated coordinate system.
|
// Transform the reference point to the rotated coordinate system.
|
||||||
|
Point p = direction.second.rotated(-direction.first);
|
||||||
|
p.x -= x_shift > 0 ? x_shift : (x_shift + line_spacing);
|
||||||
bounding_box.min.align_to_grid(
|
bounding_box.min.align_to_grid(
|
||||||
Point(this->_line_spacing, this->_line_spacing),
|
Point(line_spacing, line_spacing),
|
||||||
direction.second.rotated(-direction.first)
|
p
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate the basic pattern
|
// Find all the polygons points intersecting the rectilinear vertical lines and store
|
||||||
const coord_t x_max = bounding_box.max.x + SCALED_EPSILON;
|
// them in an std::map<> (grid) which orders them automatically by x and y.
|
||||||
Lines lines;
|
// For each intersection point we store its position (upper/lower): upper means it's
|
||||||
for (coord_t x = bounding_box.min.x; x <= x_max; x += this->_line_spacing)
|
// the upper endpoint of an intersection line, and vice versa.
|
||||||
lines.push_back(this->_line(lines.size(), x, bounding_box.min.y, bounding_box.max.y));
|
// Whenever between two intersection points we find vertices of the original polygon,
|
||||||
if (this->_horizontal_lines()) {
|
// store them in the 'skipped' member of the latter point.
|
||||||
const coord_t y_max = bounding_box.max.y + SCALED_EPSILON;
|
|
||||||
for (coord_t y = bounding_box.min.y; y <= y_max; y += this->_line_spacing)
|
|
||||||
lines.push_back(Line(Point(bounding_box.min.x, y), Point(bounding_box.max.x, y)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// clip paths against a slightly larger expolygon, so that the first and last paths
|
grid_t grid;
|
||||||
// are kept even if the expolygon has vertical sides
|
|
||||||
// the minimum offset for preventing edge lines from being clipped is SCALED_EPSILON;
|
|
||||||
// however we use a larger offset to support expolygons with slightly skewed sides and
|
|
||||||
// not perfectly straight
|
|
||||||
|
|
||||||
Polylines polylines = intersection_pl(
|
|
||||||
to_polylines(lines),
|
|
||||||
offset(expolygon, scale_(0.02)),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
|
|
||||||
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
|
|
||||||
|
|
||||||
// How much to extend an infill path from expolygon outside?
|
|
||||||
const coord_t extra = coord_t(floor(this->_min_spacing * INFILL_OVERLAP_OVER_SPACING + 0.5f));
|
|
||||||
for (Polylines::iterator it_polyline = polylines.begin();
|
|
||||||
it_polyline != polylines.end(); ++ it_polyline) {
|
|
||||||
Point *first_point = &it_polyline->points.front();
|
|
||||||
Point *last_point = &it_polyline->points.back();
|
|
||||||
if (first_point->y > last_point->y)
|
|
||||||
std::swap(first_point, last_point);
|
|
||||||
first_point->y -= extra;
|
|
||||||
last_point->y += extra;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t n_polylines_out_old = polylines_out->size();
|
|
||||||
|
|
||||||
// connect lines
|
|
||||||
if (!this->dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
|
||||||
// offset the expolygon by max(min_spacing/2, extra)
|
|
||||||
ExPolygon expolygon_off;
|
|
||||||
{
|
{
|
||||||
ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2);
|
const Polygons polygons = expolygon;
|
||||||
if (!expolygons_off.empty()) {
|
for (Polygons::const_iterator polygon = polygons.begin(); polygon != polygons.end(); ++polygon) {
|
||||||
// When expanding a polygon, the number of islands could only shrink.
|
const Points &points = polygon->points;
|
||||||
// Therefore the offset_ex shall generate exactly one expanded island
|
|
||||||
// for one input island.
|
// This vector holds the original polygon vertices found after the last intersection
|
||||||
assert(expolygons_off.size() == 1);
|
// point. We'll flush it as soon as we find the next intersection point.
|
||||||
std::swap(expolygon_off, expolygons_off.front());
|
Points skipped_points;
|
||||||
|
|
||||||
|
// This vector holds the coordinates of the intersection points found while
|
||||||
|
// looping through the polygon.
|
||||||
|
Points ips;
|
||||||
|
|
||||||
|
for (Points::const_iterator p = points.begin(); p != points.end(); ++p) {
|
||||||
|
const Point &prev = p == points.begin() ? *(points.end()-1) : *(p-1);
|
||||||
|
const Point &next = p == points.end()-1 ? *points.begin() : *(p+1);
|
||||||
|
|
||||||
|
// Does the p-next line belong to an intersection line?
|
||||||
|
if (p->x == next.x && ((p->x - bounding_box.min.x) % line_spacing) == 0) {
|
||||||
|
if (p->y == next.y) continue; // skip coinciding points
|
||||||
|
vertical_t &v = grid[p->x];
|
||||||
|
|
||||||
|
// Detect line direction.
|
||||||
|
IntersectionPoint::ipType p_type = IntersectionPoint::ipTypeLower;
|
||||||
|
IntersectionPoint::ipType n_type = IntersectionPoint::ipTypeUpper;
|
||||||
|
if (p->y > next.y) std::swap(p_type, n_type); // line goes downwards
|
||||||
|
|
||||||
|
// Do we already have 'p' in our grid?
|
||||||
|
vertical_t::iterator pit = v.find(p->y);
|
||||||
|
if (pit != v.end()) {
|
||||||
|
// Yes, we have it. If its not of the same type, it means it's
|
||||||
|
// an intermediate point of a longer line. We store this information
|
||||||
|
// for now and we'll remove it later.
|
||||||
|
if (pit->second.type != p_type)
|
||||||
|
pit->second.type = IntersectionPoint::ipTypeMiddle;
|
||||||
|
} else {
|
||||||
|
// Store the point.
|
||||||
|
IntersectionPoint ip(p->x, p->y, p_type);
|
||||||
|
v[p->y] = ip;
|
||||||
|
ips.push_back(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do we already have 'next' in our grid?
|
||||||
|
pit = v.find(next.y);
|
||||||
|
if (pit != v.end()) {
|
||||||
|
// Yes, we have it. If its not of the same type, it means it's
|
||||||
|
// an intermediate point of a longer line. We store this information
|
||||||
|
// for now and we'll remove it later.
|
||||||
|
if (pit->second.type != n_type)
|
||||||
|
pit->second.type = IntersectionPoint::ipTypeMiddle;
|
||||||
|
} else {
|
||||||
|
// Store the point.
|
||||||
|
IntersectionPoint ip(next.x, next.y, n_type);
|
||||||
|
v[next.y] = ip;
|
||||||
|
ips.push_back(ip);
|
||||||
}
|
}
|
||||||
Polylines chained = PolylineCollection::chained_path_from(
|
continue;
|
||||||
STDMOVE(polylines),
|
}
|
||||||
PolylineCollection::leftmost_point(polylines),
|
|
||||||
false // reverse allowed
|
// We're going to look for intersection points within this line.
|
||||||
);
|
// First, let's sort its x coordinates regardless of the original line direction.
|
||||||
bool first = true;
|
const coord_t min_x = std::min(p->x, next.x);
|
||||||
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
|
const coord_t max_x = std::max(p->x, next.x);
|
||||||
if (!first) {
|
|
||||||
// Try to connect the lines.
|
// Now find the leftmost intersection point belonging to the line.
|
||||||
Points &pts_end = polylines_out->back().points;
|
const coord_t min_x2 = bounding_box.min.x + ceil((double) (min_x - bounding_box.min.x) / (double)line_spacing) * (double)line_spacing;
|
||||||
const Point &first_point = it_polyline->points.front();
|
assert(min_x2 >= min_x);
|
||||||
const Point &last_point = pts_end.back();
|
|
||||||
// Distance in X, Y.
|
// In case this coordinate does not belong to this line, we have no intersection points.
|
||||||
const Vector distance = first_point.vector_to(last_point);
|
if (min_x2 > max_x) {
|
||||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
// Store the two skipped points and move on.
|
||||||
// connecting paths on the boundaries of internal regions
|
skipped_points.push_back(*p);
|
||||||
if (this->_can_connect(std::abs(distance.x), std::abs(distance.y))
|
skipped_points.push_back(next);
|
||||||
&& expolygon_off.contains(Line(last_point, first_point))) {
|
continue;
|
||||||
// Append the polyline.
|
}
|
||||||
append_to(pts_end, it_polyline->points);
|
|
||||||
|
// Find the rightmost intersection point belonging to the line.
|
||||||
|
const coord_t max_x2 = bounding_box.min.x + floor((double) (max_x - bounding_box.min.x) / (double) line_spacing) * (double)line_spacing;
|
||||||
|
assert(max_x2 <= max_x);
|
||||||
|
|
||||||
|
// We're now going past the first point, so save it.
|
||||||
|
const bool line_goes_right = next.x > p->x;
|
||||||
|
if (line_goes_right ? (p->x < min_x2) : (p->x > max_x2))
|
||||||
|
skipped_points.push_back(*p);
|
||||||
|
|
||||||
|
// Now loop through those intersection points according the original direction
|
||||||
|
// of the line (because we need to store them in this order).
|
||||||
|
for (coord_t x = line_goes_right ? min_x2 : max_x2;
|
||||||
|
x >= min_x && x <= max_x;
|
||||||
|
x += line_goes_right ? +line_spacing : -line_spacing) {
|
||||||
|
|
||||||
|
// Is this intersection an endpoint of the original line *and* is the
|
||||||
|
// intersection just a tangent point? If so, just skip it.
|
||||||
|
if (x == p->x && ((prev.x > x && next.x > x) || (prev.x < x && next.x < x))) {
|
||||||
|
skipped_points.push_back(*p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (x == next.x) {
|
||||||
|
const Point &next2 = p == (points.end()-2) ? *points.begin()
|
||||||
|
: p == (points.end()-1) ? *(points.begin()+1) : *(p+2);
|
||||||
|
if ((p->x > x && next2.x > x) || (p->x < x && next2.x < x)) {
|
||||||
|
skipped_points.push_back(next);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The lines cannot be connected.
|
|
||||||
#if SLIC3R_CPPVER >= 11
|
// Calculate the y coordinate of this intersection.
|
||||||
polylines_out->push_back(std::move(*it_polyline));
|
IntersectionPoint ip(
|
||||||
#else
|
x,
|
||||||
polylines_out->push_back(Polyline());
|
p->y + double(next.y - p->y) * double(x - p->x) / double(next.x - p->x),
|
||||||
std::swap(polylines_out->back(), *it_polyline);
|
line_goes_right ? IntersectionPoint::ipTypeLower : IntersectionPoint::ipTypeUpper
|
||||||
#endif
|
);
|
||||||
first = false;
|
vertical_t &v = grid[ip.x];
|
||||||
|
|
||||||
|
// Did we already find this point?
|
||||||
|
// (We might have found it as the endpoint of a vertical line.)
|
||||||
|
{
|
||||||
|
vertical_t::iterator pit = v.find(ip.y);
|
||||||
|
if (pit != v.end()) {
|
||||||
|
// Yes, we have it. If its not of the same type, it means it's
|
||||||
|
// an intermediate point of a longer line. We store this information
|
||||||
|
// for now and we'll remove it later.
|
||||||
|
if (pit->second.type != ip.type)
|
||||||
|
pit->second.type = IntersectionPoint::ipTypeMiddle;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store the skipped polygon vertices along with this point.
|
||||||
|
ip.skipped = skipped_points;
|
||||||
|
skipped_points.clear();
|
||||||
|
|
||||||
|
#ifdef DEBUG_RECTILINEAR
|
||||||
|
printf("NEW POINT at %f,%f\n", unscale(ip.x), unscale(ip.y));
|
||||||
|
for (Points::const_iterator it = ip.skipped.begin(); it != ip.skipped.end(); ++it)
|
||||||
|
printf(" skipped: %f,%f\n", unscale(it->x), unscale(it->y));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Store the point.
|
||||||
|
v[ip.y] = ip;
|
||||||
|
ips.push_back(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're now going past the final point, so save it.
|
||||||
|
if (line_goes_right ? (next.x > max_x2) : (next.x < min_x2))
|
||||||
|
skipped_points.push_back(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->dont_connect) {
|
||||||
|
// We'll now build connections between the vertical intersection lines.
|
||||||
|
// Each intersection point will be connected to the first intersection point
|
||||||
|
// found along the original polygon having a greater x coordinate (or the same
|
||||||
|
// x coordinate: think about two vertical intersection lines having the same x
|
||||||
|
// separated by a hole polygon: we'll connect them with the hole portion).
|
||||||
|
// We will sweep only from left to right, so we only need to build connections
|
||||||
|
// in this direction.
|
||||||
|
for (Points::const_iterator it = ips.begin(); it != ips.end(); ++it) {
|
||||||
|
IntersectionPoint &ip = grid[it->x][it->y];
|
||||||
|
IntersectionPoint &next = it == ips.end()-1 ? grid[ips.begin()->x][ips.begin()->y] : grid[(it+1)->x][(it+1)->y];
|
||||||
|
|
||||||
|
#ifdef DEBUG_RECTILINEAR
|
||||||
|
printf("CONNECTING %f,%f to %f,%f\n",
|
||||||
|
unscale(ip.x), unscale(ip.y),
|
||||||
|
unscale(next.x), unscale(next.y)
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// We didn't flush the skipped_points vector after completing the loop above:
|
||||||
|
// it now contains the polygon vertices between the last and the first
|
||||||
|
// intersection points.
|
||||||
|
if (it == ips.begin())
|
||||||
|
ip.skipped.insert(ip.skipped.begin(), skipped_points.begin(), skipped_points.end());
|
||||||
|
|
||||||
|
if (ip.x <= next.x) {
|
||||||
|
// Link 'ip' to 'next' --->
|
||||||
|
if (ip.next.empty()) {
|
||||||
|
ip.next = next.skipped;
|
||||||
|
ip.next.push_back(next);
|
||||||
|
}
|
||||||
|
} else if (next.x < ip.x) {
|
||||||
|
// Link 'next' to 'ip' --->
|
||||||
|
if (next.next.empty()) {
|
||||||
|
next.next = next.skipped;
|
||||||
|
std::reverse(next.next.begin(), next.next.end());
|
||||||
|
next.next.push_back(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do some cleanup: remove the 'skipped' points we used for building
|
||||||
|
// connections and also remove the middle intersection points.
|
||||||
|
for (Points::const_iterator it = ips.begin(); it != ips.end(); ++it) {
|
||||||
|
vertical_t &v = grid[it->x];
|
||||||
|
IntersectionPoint &ip = v[it->y];
|
||||||
|
ip.skipped.clear();
|
||||||
|
if (ip.type == IntersectionPoint::ipTypeMiddle)
|
||||||
|
v.erase(it->y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_RECTILINEAR
|
||||||
|
SVG svg("grid.svg");
|
||||||
|
svg.draw(expolygon);
|
||||||
|
|
||||||
|
printf("GRID:\n");
|
||||||
|
for (grid_t::const_iterator it = grid.begin(); it != grid.end(); ++it) {
|
||||||
|
printf("x = %f:\n", unscale(it->first));
|
||||||
|
for (vertical_t::const_iterator v = it->second.begin(); v != it->second.end(); ++v) {
|
||||||
|
const IntersectionPoint &ip = v->second;
|
||||||
|
printf(" y = %f (%s, next = %f,%f, extra = %zu)\n", unscale(v->first),
|
||||||
|
ip.type == IntersectionPoint::ipTypeLower ? "lower"
|
||||||
|
: ip.type == IntersectionPoint::ipTypeMiddle ? "middle" : "upper",
|
||||||
|
(ip.next.empty() ? -1 : unscale(ip.next.back().x)),
|
||||||
|
(ip.next.empty() ? -1 : unscale(ip.next.back().y)),
|
||||||
|
(ip.next.empty() ? 0 : ip.next.size()-1)
|
||||||
|
);
|
||||||
|
svg.draw(ip, ip.type == IntersectionPoint::ipTypeLower ? "blue"
|
||||||
|
: ip.type == IntersectionPoint::ipTypeMiddle ? "yellow" : "red");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
svg.Close();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Calculate the extension of the vertical endpoints according to the configured value.
|
||||||
|
const coord_t extra_y = floor((double)min_spacing * this->endpoints_overlap + 0.5f);
|
||||||
|
|
||||||
|
// Store the number of polygons already existing in the output container.
|
||||||
|
const size_t n_polylines_out_old = out->size();
|
||||||
|
|
||||||
|
// Loop until we have no more vertical lines available.
|
||||||
|
while (!grid.empty()) {
|
||||||
|
// Get the first x coordinate.
|
||||||
|
vertical_t &v = grid.begin()->second;
|
||||||
|
|
||||||
|
// If this x coordinate does not have any y coordinate, remove it.
|
||||||
|
if (v.empty()) {
|
||||||
|
grid.erase(grid.begin());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect every x coordinate to contain an even number of y coordinates
|
||||||
|
// because they are the endpoints of vertical intersection lines:
|
||||||
|
// lower/upper, lower/upper etc.
|
||||||
|
assert(v.size() % 2 == 0);
|
||||||
|
|
||||||
|
// Get the first lower point.
|
||||||
|
vertical_t::iterator it = v.begin(); // minimum x,y
|
||||||
|
IntersectionPoint p = it->second;
|
||||||
|
assert(p.type == IntersectionPoint::ipTypeLower);
|
||||||
|
|
||||||
|
// Start our polyline.
|
||||||
|
Polyline polyline;
|
||||||
|
polyline.append(p);
|
||||||
|
polyline.points.back().y -= extra_y;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Complete the vertical line by finding the corresponding upper or lower point.
|
||||||
|
if (p.type == IntersectionPoint::ipTypeUpper) {
|
||||||
|
// find first point along c.x with y < c.y
|
||||||
|
assert(it != grid[p.x].begin());
|
||||||
|
--it;
|
||||||
|
} else {
|
||||||
|
// find first point along c.x with y > c.y
|
||||||
|
++it;
|
||||||
|
assert(it != grid[p.x].end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the point to our polyline.
|
||||||
|
IntersectionPoint b = it->second;
|
||||||
|
assert(b.type != p.type);
|
||||||
|
polyline.append(b);
|
||||||
|
polyline.points.back().y += extra_y * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1);
|
||||||
|
|
||||||
|
// Remove the two endpoints of this vertical line from the grid.
|
||||||
|
{
|
||||||
|
vertical_t &v = grid[p.x];
|
||||||
|
v.erase(p.y);
|
||||||
|
v.erase(it);
|
||||||
|
if (v.empty()) grid.erase(p.x);
|
||||||
|
}
|
||||||
|
// Do we have a connection starting from here?
|
||||||
|
// If not, stop the polyline.
|
||||||
|
if (b.next.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
// If we have a connection, append it to the polyline.
|
||||||
|
// We apply the y extension to the whole connection line. This works well when
|
||||||
|
// the connection is straight and horizontal, but doesn't work well when the
|
||||||
|
// connection is articulated and also has vertical parts.
|
||||||
|
{
|
||||||
|
// TODO: here's where we should check for overextrusion. We should only add
|
||||||
|
// connection points while they are not generating vertical lines within the
|
||||||
|
// extrusion thickness of the main vertical lines. We should also check whether
|
||||||
|
// a previous run of this method occupied this polygon portion (derived infill
|
||||||
|
// patterns doing multiple runs at different angles generate overlapping connections).
|
||||||
|
// In both cases, we should just stop the connection and break the polyline here.
|
||||||
|
const size_t n = polyline.points.size();
|
||||||
|
polyline.append(b.next);
|
||||||
|
for (Points::iterator pit = polyline.points.begin()+n; pit != polyline.points.end(); ++pit)
|
||||||
|
pit->y += extra_y * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the final point still available?
|
||||||
|
if (grid.count(b.next.back().x) == 0
|
||||||
|
|| grid[b.next.back().x].count(b.next.back().y) == 0)
|
||||||
|
// We already used this point or we might have removed this
|
||||||
|
// point while building the grid because it's collinear (middle); in either
|
||||||
|
// cases the connection line from the previous one is legit and worth having.
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Retrieve the intersection point. The next loop will find the correspondent
|
||||||
|
// endpoint of the vertical line.
|
||||||
|
it = grid[ b.next.back().x ].find(b.next.back().y);
|
||||||
|
p = it->second;
|
||||||
|
|
||||||
|
// If the connection brought us to another x coordinate, we expect the point
|
||||||
|
// type to be the same.
|
||||||
|
assert((p.type == b.type && p.x > b.x)
|
||||||
|
|| (p.type != b.type && p.x == b.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yay, we have a polyline!
|
||||||
|
if (polyline.is_valid())
|
||||||
|
out->push_back(polyline);
|
||||||
|
}
|
||||||
|
|
||||||
// paths must be rotated back
|
// paths must be rotated back
|
||||||
for (Polylines::iterator it = polylines_out->begin() + n_polylines_out_old;
|
for (Polylines::iterator it = out->begin() + n_polylines_out_old;
|
||||||
it != polylines_out->end(); ++ it) {
|
it != out->end(); ++it)
|
||||||
// No need to translate, the absolute position is irrelevant.
|
|
||||||
// it->translate(- direction.second.x, - direction.second.y);
|
|
||||||
it->rotate(direction.first);
|
it->rotate(direction.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FillRectilinear::_fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const direction_t &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* out)
|
||||||
|
{
|
||||||
|
this->_fill_single_direction(expolygon, direction, 0, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillGrid::_fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const direction_t &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* out)
|
||||||
|
{
|
||||||
|
FillGrid fill2 = *this;
|
||||||
|
fill2.density /= 2.;
|
||||||
|
|
||||||
|
direction_t direction2 = direction;
|
||||||
|
direction2.first += PI/2;
|
||||||
|
fill2._fill_single_direction(expolygon, direction, 0, out);
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, 0, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillTriangles::_fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const direction_t &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* out)
|
||||||
|
{
|
||||||
|
FillTriangles fill2 = *this;
|
||||||
|
fill2.density /= 3.;
|
||||||
|
direction_t direction2 = direction;
|
||||||
|
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, 0, out);
|
||||||
|
|
||||||
|
direction2.first += PI/3;
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, 0, out);
|
||||||
|
|
||||||
|
direction2.first += PI/3;
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, 0, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillStars::_fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const direction_t &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* out)
|
||||||
|
{
|
||||||
|
FillStars fill2 = *this;
|
||||||
|
fill2.density /= 3.;
|
||||||
|
direction_t direction2 = direction;
|
||||||
|
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, 0, out);
|
||||||
|
|
||||||
|
direction2.first += PI/3;
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, 0, out);
|
||||||
|
|
||||||
|
direction2.first += PI/3;
|
||||||
|
const coord_t x_shift = 0.5 * scale_(fill2.spacing) / fill2.density;
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, x_shift, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillCubic::_fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const direction_t &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* out)
|
||||||
|
{
|
||||||
|
FillCubic fill2 = *this;
|
||||||
|
fill2.density /= 3.;
|
||||||
|
direction_t direction2 = direction;
|
||||||
|
|
||||||
|
const coord_t range = scale_(this->spacing / this->density);
|
||||||
|
const coord_t x_shift = abs(( (coord_t)(scale_(this->z) + range) % (coord_t)(range * 2)) - range);
|
||||||
|
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, -x_shift, out);
|
||||||
|
|
||||||
|
direction2.first += PI/3;
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, +x_shift, out);
|
||||||
|
|
||||||
|
direction2.first += PI/3;
|
||||||
|
fill2._fill_single_direction(expolygon, direction2, -x_shift, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -16,61 +16,33 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
virtual void _fill_surface_single(
|
virtual void _fill_surface_single(
|
||||||
unsigned int thickness_layers,
|
unsigned int thickness_layers,
|
||||||
const std::pair<float, Point> &direction,
|
const direction_t &direction,
|
||||||
ExPolygon &expolygon,
|
ExPolygon &expolygon,
|
||||||
Polylines* polylines_out);
|
Polylines* polylines_out);
|
||||||
|
|
||||||
coord_t _min_spacing;
|
void _fill_single_direction(ExPolygon expolygon, const direction_t &direction,
|
||||||
coord_t _line_spacing;
|
coord_t x_shift, Polylines* out);
|
||||||
// distance threshold for allowing the horizontal infill lines to be connected into a continuous path
|
|
||||||
coord_t _diagonal_distance;
|
|
||||||
// only for line infill
|
|
||||||
coord_t _line_oscillation;
|
|
||||||
|
|
||||||
// Enabled for the grid infill, disabled for the rectilinear and line infill.
|
struct IntersectionPoint : Point {
|
||||||
virtual bool _horizontal_lines() const { return false; };
|
enum ipType { ipTypeLower, ipTypeUpper, ipTypeMiddle };
|
||||||
|
ipType type;
|
||||||
|
|
||||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const
|
// skipped contains the polygon points accumulated between the previous intersection
|
||||||
{ return Line(Point(x, y_min), Point(x, y_max)); };
|
// point and the current one, in the original polygon winding order (does not contain
|
||||||
|
// either points)
|
||||||
|
Points skipped;
|
||||||
|
|
||||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) {
|
// next contains a polygon portion connecting this point to the first intersection
|
||||||
return dist_X <= this->_diagonal_distance
|
// point found following the polygon in any direction but having:
|
||||||
&& dist_Y <= this->_diagonal_distance;
|
// x > this->x || (x == this->x && y > this->y)
|
||||||
|
// (it doesn't contain *this but it contains the target intersection point)
|
||||||
|
Points next;
|
||||||
|
|
||||||
|
IntersectionPoint() : Point() {};
|
||||||
|
IntersectionPoint(coord_t x, coord_t y, ipType _type) : Point(x,y), type(_type) {};
|
||||||
};
|
};
|
||||||
};
|
typedef std::map<coord_t,IntersectionPoint> vertical_t; // <y,point>
|
||||||
|
typedef std::map<coord_t,vertical_t> grid_t; // <x,<y,point>>
|
||||||
class FillLine : public FillRectilinear
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Fill* clone() const { return new FillLine(*this); };
|
|
||||||
virtual ~FillLine() {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const {
|
|
||||||
coord_t osc = (i & 1) ? this->_line_oscillation : 0;
|
|
||||||
return Line(Point(x - osc, y_min), Point(x + osc, y_max));
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y)
|
|
||||||
{
|
|
||||||
coord_t TOLERANCE = 10 * SCALED_EPSILON;
|
|
||||||
return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE)
|
|
||||||
&& (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE)
|
|
||||||
&& (dist_Y <= this->_diagonal_distance);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class FillGrid : public FillRectilinear
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Fill* clone() const { return new FillGrid(*this); };
|
|
||||||
virtual ~FillGrid() {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill.
|
|
||||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
|
||||||
// Flag for Slic3r::Fill::Rectilinear to fill both directions.
|
|
||||||
virtual bool _horizontal_lines() const { return true; };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FillAlignedRectilinear : public FillRectilinear
|
class FillAlignedRectilinear : public FillRectilinear
|
||||||
@ -84,6 +56,74 @@ protected:
|
|||||||
virtual float _layer_angle(size_t idx) const { return 0.f; };
|
virtual float _layer_angle(size_t idx) const { return 0.f; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FillGrid : public FillRectilinear
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Fill* clone() const { return new FillGrid(*this); };
|
||||||
|
virtual ~FillGrid() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill.
|
||||||
|
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||||
|
|
||||||
|
virtual void _fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const std::pair<float, Point> &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* polylines_out);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FillTriangles : public FillRectilinear
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Fill* clone() const { return new FillTriangles(*this); };
|
||||||
|
virtual ~FillTriangles() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill.
|
||||||
|
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||||
|
|
||||||
|
virtual void _fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const std::pair<float, Point> &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* polylines_out);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FillStars : public FillRectilinear
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Fill* clone() const { return new FillStars(*this); };
|
||||||
|
virtual ~FillStars() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill.
|
||||||
|
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||||
|
|
||||||
|
virtual void _fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const std::pair<float, Point> &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* polylines_out);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FillCubic : public FillRectilinear
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Fill* clone() const { return new FillCubic(*this); };
|
||||||
|
virtual ~FillCubic() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill.
|
||||||
|
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||||
|
|
||||||
|
virtual void _fill_surface_single(
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const std::pair<float, Point> &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines* polylines_out);
|
||||||
|
};
|
||||||
|
|
||||||
}; // namespace Slic3r
|
}; // namespace Slic3r
|
||||||
|
|
||||||
#endif // slic3r_FillRectilinear_hpp_
|
#endif // slic3r_FillRectilinear_hpp_
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,81 +0,0 @@
|
|||||||
#ifndef slic3r_FillRectilinear2_hpp_
|
|
||||||
#define slic3r_FillRectilinear2_hpp_
|
|
||||||
|
|
||||||
#include "../libslic3r.h"
|
|
||||||
|
|
||||||
#include "Fill.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
|
||||||
|
|
||||||
class Surface;
|
|
||||||
|
|
||||||
class FillRectilinear2 : public Fill
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Fill* clone() const { return new FillRectilinear2(*this); };
|
|
||||||
virtual ~FillRectilinear2() {}
|
|
||||||
virtual Polylines fill_surface(const Surface &surface);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool fill_surface_by_lines(const Surface *surface, float angleBase, float pattern_shift, Polylines &polylines_out);
|
|
||||||
};
|
|
||||||
|
|
||||||
class FillGrid2 : public FillRectilinear2
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Fill* clone() const { return new FillGrid2(*this); };
|
|
||||||
virtual ~FillGrid2() {}
|
|
||||||
virtual Polylines fill_surface(const Surface &surface);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
|
||||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class FillTriangles : public FillRectilinear2
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Fill* clone() const { return new FillTriangles(*this); };
|
|
||||||
virtual ~FillTriangles() {}
|
|
||||||
virtual Polylines fill_surface(const Surface &surface);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
|
||||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class FillStars : public FillRectilinear2
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Fill* clone() const { return new FillStars(*this); };
|
|
||||||
virtual ~FillStars() {}
|
|
||||||
virtual Polylines fill_surface(const Surface &surface);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
|
||||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class FillCubic : public FillRectilinear2
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual Fill* clone() const { return new FillCubic(*this); };
|
|
||||||
virtual ~FillCubic() {}
|
|
||||||
virtual Polylines fill_surface(const Surface &surface);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
|
||||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Remove sticks (tentacles with zero area) from the polygon.
|
|
||||||
extern bool remove_sticks(Polygon &poly);
|
|
||||||
extern bool remove_sticks(Polygons &polys);
|
|
||||||
extern bool remove_sticks(ExPolygon &poly);
|
|
||||||
extern bool remove_small(Polygons &polys, double min_area);
|
|
||||||
|
|
||||||
|
|
||||||
}; // namespace Slic3r
|
|
||||||
|
|
||||||
#endif // slic3r_FillRectilinear2_hpp_
|
|
@ -549,25 +549,6 @@ MedialAxis::build(ThickPolylines* polylines)
|
|||||||
// append polyline to result
|
// append polyline to result
|
||||||
polylines->push_back(polyline);
|
polylines->push_back(polyline);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG
|
|
||||||
{
|
|
||||||
char path[2048];
|
|
||||||
static int iRun = 0;
|
|
||||||
sprintf(path, "out/MedialAxis-%d.svg", iRun ++);
|
|
||||||
//dump_voronoi_to_svg(this->lines, this->vd, polylines, path);
|
|
||||||
|
|
||||||
|
|
||||||
printf("Thick lines: ");
|
|
||||||
for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) {
|
|
||||||
ThickLines lines = it->thicklines();
|
|
||||||
for (ThickLines::const_iterator it2 = lines.begin(); it2 != lines.end(); ++ it2) {
|
|
||||||
printf("%f,%f ", it2->a_width, it2->b_width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
#endif /* SLIC3R_DEBUG */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -391,9 +391,6 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->enum_values.push_back("rectilinear");
|
def->enum_values.push_back("rectilinear");
|
||||||
def->enum_values.push_back("alignedrectilinear");
|
def->enum_values.push_back("alignedrectilinear");
|
||||||
def->enum_values.push_back("grid");
|
def->enum_values.push_back("grid");
|
||||||
def->enum_values.push_back("line");
|
|
||||||
def->enum_values.push_back("rectilinear2");
|
|
||||||
def->enum_values.push_back("grid2");
|
|
||||||
def->enum_values.push_back("triangles");
|
def->enum_values.push_back("triangles");
|
||||||
def->enum_values.push_back("stars");
|
def->enum_values.push_back("stars");
|
||||||
def->enum_values.push_back("cubic");
|
def->enum_values.push_back("cubic");
|
||||||
@ -406,9 +403,6 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->enum_labels.push_back("Rectilinear");
|
def->enum_labels.push_back("Rectilinear");
|
||||||
def->enum_labels.push_back("Aligned Rectilinear");
|
def->enum_labels.push_back("Aligned Rectilinear");
|
||||||
def->enum_labels.push_back("Grid");
|
def->enum_labels.push_back("Grid");
|
||||||
def->enum_labels.push_back("Line");
|
|
||||||
def->enum_labels.push_back("Rectilinear 2");
|
|
||||||
def->enum_labels.push_back("Grid 2");
|
|
||||||
def->enum_labels.push_back("Triangles");
|
def->enum_labels.push_back("Triangles");
|
||||||
def->enum_labels.push_back("Stars");
|
def->enum_labels.push_back("Stars");
|
||||||
def->enum_labels.push_back("Cubic");
|
def->enum_labels.push_back("Cubic");
|
||||||
@ -418,7 +412,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->enum_labels.push_back("Hilbert Curve");
|
def->enum_labels.push_back("Hilbert Curve");
|
||||||
def->enum_labels.push_back("Archimedean Chords");
|
def->enum_labels.push_back("Archimedean Chords");
|
||||||
def->enum_labels.push_back("Octagram Spiral");
|
def->enum_labels.push_back("Octagram Spiral");
|
||||||
def->default_value = new ConfigOptionEnum<InfillPattern>(ipHoneycomb);
|
def->default_value = new ConfigOptionEnum<InfillPattern>(ipStars);
|
||||||
|
|
||||||
def = this->add("first_layer_acceleration", coFloat);
|
def = this->add("first_layer_acceleration", coFloat);
|
||||||
def->label = "First layer";
|
def->label = "First layer";
|
||||||
|
@ -30,8 +30,8 @@ enum GCodeFlavor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum InfillPattern {
|
enum InfillPattern {
|
||||||
ipRectilinear, ipGrid, ipLine, ipAlignedRectilinear,
|
ipRectilinear, ipGrid, ipAlignedRectilinear,
|
||||||
ipRectilinear2, ipGrid2, ipTriangles, ipStars, ipCubic,
|
ipTriangles, ipStars, ipCubic,
|
||||||
ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||||
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
|
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
|
||||||
};
|
};
|
||||||
@ -63,9 +63,6 @@ template<> inline t_config_enum_values ConfigOptionEnum<InfillPattern>::get_enum
|
|||||||
keys_map["rectilinear"] = ipRectilinear;
|
keys_map["rectilinear"] = ipRectilinear;
|
||||||
keys_map["alignedrectilinear"] = ipAlignedRectilinear;
|
keys_map["alignedrectilinear"] = ipAlignedRectilinear;
|
||||||
keys_map["grid"] = ipGrid;
|
keys_map["grid"] = ipGrid;
|
||||||
keys_map["line"] = ipLine;
|
|
||||||
keys_map["rectilinear2"] = ipRectilinear2;
|
|
||||||
keys_map["grid2"] = ipGrid2;
|
|
||||||
keys_map["triangles"] = ipTriangles;
|
keys_map["triangles"] = ipTriangles;
|
||||||
keys_map["stars"] = ipStars;
|
keys_map["stars"] = ipStars;
|
||||||
keys_map["cubic"] = ipCubic;
|
keys_map["cubic"] = ipCubic;
|
||||||
|
@ -97,8 +97,8 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
|
|||||||
$config->set_deserialize('gcode_flavor', 'machinekit');
|
$config->set_deserialize('gcode_flavor', 'machinekit');
|
||||||
is $config->get('gcode_flavor'), 'machinekit', 'deserialize enum (gcode_flavor)';
|
is $config->get('gcode_flavor'), 'machinekit', 'deserialize enum (gcode_flavor)';
|
||||||
|
|
||||||
$config->set_deserialize('fill_pattern', 'line');
|
$config->set_deserialize('fill_pattern', 'stars');
|
||||||
is $config->get('fill_pattern'), 'line', 'deserialize enum (fill_pattern)';
|
is $config->get('fill_pattern'), 'stars', 'deserialize enum (fill_pattern)';
|
||||||
|
|
||||||
$config->set_deserialize('support_material_pattern', 'pillars');
|
$config->set_deserialize('support_material_pattern', 'pillars');
|
||||||
is $config->get('support_material_pattern'), 'pillars', 'deserialize enum (support_material_pattern)';
|
is $config->get('support_material_pattern'), 'pillars', 'deserialize enum (support_material_pattern)';
|
||||||
@ -199,12 +199,12 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
|
|||||||
|
|
||||||
{
|
{
|
||||||
my $config = Slic3r::Config->new;
|
my $config = Slic3r::Config->new;
|
||||||
$config->set('fill_pattern', 'line');
|
$config->set('fill_pattern', 'stars');
|
||||||
|
|
||||||
my $config2 = Slic3r::Config->new;
|
my $config2 = Slic3r::Config->new;
|
||||||
$config2->set('fill_pattern', 'hilbertcurve');
|
$config2->set('fill_pattern', 'hilbertcurve');
|
||||||
|
|
||||||
is $config->get('fill_pattern'), 'line', 'no interferences between DynamicConfig objects';
|
is $config->get('fill_pattern'), 'stars', 'no interferences between DynamicConfig objects';
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -13,10 +13,17 @@
|
|||||||
|
|
||||||
void set_bounding_box(BoundingBox *bbox)
|
void set_bounding_box(BoundingBox *bbox)
|
||||||
%code{% THIS->fill->bounding_box = *bbox; %};
|
%code{% THIS->fill->bounding_box = *bbox; %};
|
||||||
|
|
||||||
void set_spacing(coordf_t spacing)
|
void set_spacing(coordf_t spacing)
|
||||||
%code{% THIS->fill->spacing = spacing; %};
|
%code{% THIS->fill->spacing = spacing; %};
|
||||||
coordf_t spacing()
|
coordf_t spacing()
|
||||||
%code{% RETVAL = THIS->fill->spacing; %};
|
%code{% RETVAL = THIS->fill->spacing; %};
|
||||||
|
|
||||||
|
void set_endpoints_overlap(float overlap)
|
||||||
|
%code{% THIS->fill->endpoints_overlap = overlap; %};
|
||||||
|
float endpoints_overlap()
|
||||||
|
%code{% RETVAL = THIS->fill->endpoints_overlap; %};
|
||||||
|
|
||||||
void set_layer_id(size_t layer_id)
|
void set_layer_id(size_t layer_id)
|
||||||
%code{% THIS->fill->layer_id = layer_id; %};
|
%code{% THIS->fill->layer_id = layer_id; %};
|
||||||
void set_z(coordf_t z)
|
void set_z(coordf_t z)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user