mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-25 15:44:12 -06:00
WIP: Moved sources int src/, separated most of the source code from Perl.
The XS was left only for the unit / integration tests, and it links libslic3r only. No wxWidgets are allowed to be used from Perl starting from now.
This commit is contained in:
parent
3ddaccb641
commit
0558b53493
1706 changed files with 7413 additions and 7638 deletions
271
src/libslic3r/Fill/Fill.cpp
Normal file
271
src/libslic3r/Fill/Fill.cpp
Normal file
|
@ -0,0 +1,271 @@
|
|||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <memory>
|
||||
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
#include "../Layer.hpp"
|
||||
#include "../Print.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct SurfaceGroupAttrib
|
||||
{
|
||||
SurfaceGroupAttrib() : is_solid(false), flow_width(0.f), pattern(-1) {}
|
||||
bool operator==(const SurfaceGroupAttrib &other) const
|
||||
{ return is_solid == other.is_solid && flow_width == other.flow_width && pattern == other.pattern; }
|
||||
bool is_solid;
|
||||
float flow_width;
|
||||
// pattern is of type InfillPattern, -1 for an unset pattern.
|
||||
int pattern;
|
||||
};
|
||||
|
||||
// Generate infills for Slic3r::Layer::Region.
|
||||
// The Slic3r::Layer::Region at this point of time may contain
|
||||
// surfaces of various types (internal/bridge/top/bottom/solid).
|
||||
// The infills are generated on the groups of surfaces with a compatible type.
|
||||
// Returns an array of Slic3r::ExtrusionPath::Collection objects containing the infills generaed now
|
||||
// and the thin fills generated by generate_perimeters().
|
||||
void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
||||
{
|
||||
// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
|
||||
|
||||
double fill_density = layerm.region()->config().fill_density;
|
||||
Flow infill_flow = layerm.flow(frInfill);
|
||||
Flow solid_infill_flow = layerm.flow(frSolidInfill);
|
||||
Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill);
|
||||
|
||||
Surfaces surfaces;
|
||||
|
||||
// merge adjacent surfaces
|
||||
// in case of bridge surfaces, the ones with defined angle will be attached to the ones
|
||||
// without any angle (shouldn't this logic be moved to process_external_surfaces()?)
|
||||
{
|
||||
Polygons polygons_bridged;
|
||||
polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size());
|
||||
for (Surfaces::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it)
|
||||
if (it->bridge_angle >= 0)
|
||||
polygons_append(polygons_bridged, *it);
|
||||
|
||||
// group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
|
||||
// group is of type Slic3r::SurfaceCollection
|
||||
//FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
|
||||
std::vector<SurfacesPtr> groups;
|
||||
layerm.fill_surfaces.group(&groups);
|
||||
|
||||
// merge compatible groups (we can generate continuous infill for them)
|
||||
{
|
||||
// cache flow widths and patterns used for all solid groups
|
||||
// (we'll use them for comparing compatible groups)
|
||||
std::vector<SurfaceGroupAttrib> group_attrib(groups.size());
|
||||
for (size_t i = 0; i < groups.size(); ++ i) {
|
||||
// we can only merge solid non-bridge surfaces, so discard
|
||||
// non-solid surfaces
|
||||
const Surface &surface = *groups[i].front();
|
||||
if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
|
||||
group_attrib[i].is_solid = true;
|
||||
group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
|
||||
group_attrib[i].pattern = surface.is_external() ? layerm.region()->config().external_fill_pattern.value : ipRectilinear;
|
||||
}
|
||||
}
|
||||
// Loop through solid groups, find compatible groups and append them to this one.
|
||||
for (size_t i = 0; i < groups.size(); ++ i) {
|
||||
if (! group_attrib[i].is_solid)
|
||||
continue;
|
||||
for (size_t j = i + 1; j < groups.size();) {
|
||||
if (group_attrib[i] == group_attrib[j]) {
|
||||
// groups are compatible, merge them
|
||||
groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end());
|
||||
groups.erase(groups.begin() + j);
|
||||
group_attrib.erase(group_attrib.begin() + j);
|
||||
} else
|
||||
++ j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round.
|
||||
for (size_t round = 0; round < 2; ++ round) {
|
||||
for (std::vector<SurfacesPtr>::iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) {
|
||||
const SurfacesPtr &group = *it_group;
|
||||
bool is_bridge = group.front()->bridge_angle >= 0;
|
||||
if (is_bridge != (round == 0))
|
||||
continue;
|
||||
// Make a union of polygons defining the infiill regions of a group, use a safety offset.
|
||||
Polygons union_p = union_(to_polygons(*it_group), true);
|
||||
// Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
|
||||
if (! polygons_bridged.empty() && ! is_bridge)
|
||||
union_p = diff(union_p, polygons_bridged, true);
|
||||
// subtract any other surface already processed
|
||||
//FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
|
||||
// Using group.front() as a template.
|
||||
surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we need to detect any narrow surfaces that might collapse
|
||||
// when adding spacing below
|
||||
// such narrow surfaces are often generated in sloping walls
|
||||
// by bridge_over_infill() and combine_infill() as a result of the
|
||||
// subtraction of the combinable area from the layer infill area,
|
||||
// which leaves small areas near the perimeters
|
||||
// we are going to grow such regions by overlapping them with the void (if any)
|
||||
// TODO: detect and investigate whether there could be narrow regions without
|
||||
// any void neighbors
|
||||
{
|
||||
coord_t distance_between_surfaces = std::max(
|
||||
std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()),
|
||||
top_solid_infill_flow.scaled_spacing());
|
||||
Polygons surfaces_polygons = to_polygons(surfaces);
|
||||
Polygons collapsed = diff(
|
||||
surfaces_polygons,
|
||||
offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2),
|
||||
true);
|
||||
Polygons to_subtract;
|
||||
to_subtract.reserve(collapsed.size() + number_polygons(surfaces));
|
||||
for (Surfaces::const_iterator it_surface = surfaces.begin(); it_surface != surfaces.end(); ++ it_surface)
|
||||
if (it_surface->surface_type == stInternalVoid)
|
||||
polygons_append(to_subtract, *it_surface);
|
||||
polygons_append(to_subtract, collapsed);
|
||||
surfaces_append(
|
||||
surfaces,
|
||||
intersection_ex(
|
||||
offset(collapsed, distance_between_surfaces),
|
||||
to_subtract,
|
||||
true),
|
||||
stInternalSolid);
|
||||
}
|
||||
|
||||
if (0) {
|
||||
// require "Slic3r/SVG.pm";
|
||||
// Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
|
||||
// expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
|
||||
// red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ],
|
||||
// );
|
||||
}
|
||||
|
||||
for (const Surface &surface : surfaces) {
|
||||
if (surface.surface_type == stInternalVoid)
|
||||
continue;
|
||||
InfillPattern fill_pattern = layerm.region()->config().fill_pattern.value;
|
||||
double density = fill_density;
|
||||
FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill :
|
||||
(surface.is_solid() ? frSolidInfill : frInfill);
|
||||
bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
|
||||
|
||||
if (surface.is_solid()) {
|
||||
density = 100.;
|
||||
fill_pattern = (surface.is_external() && ! is_bridge) ?
|
||||
layerm.region()->config().external_fill_pattern.value :
|
||||
ipRectilinear;
|
||||
} else if (density <= 0)
|
||||
continue;
|
||||
|
||||
// get filler object
|
||||
std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern));
|
||||
f->set_bounding_box(layerm.layer()->object()->bounding_box());
|
||||
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness;
|
||||
Flow flow = layerm.region()->flow(
|
||||
role,
|
||||
h,
|
||||
is_bridge || f->use_bridge_flow(), // bridge flow?
|
||||
layerm.layer()->id() == 0, // first layer?
|
||||
-1, // auto width
|
||||
*layerm.layer()->object()
|
||||
);
|
||||
|
||||
// calculate flow spacing for infill pattern generation
|
||||
bool using_internal_flow = false;
|
||||
if (! surface.is_solid() && ! is_bridge) {
|
||||
// it's internal infill, so we can calculate a generic flow spacing
|
||||
// for all layers, for avoiding the ugly effect of
|
||||
// misaligned infill on first layer because of different extrusion width and
|
||||
// layer height
|
||||
Flow internal_flow = layerm.region()->flow(
|
||||
frInfill,
|
||||
layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers?
|
||||
false, // no bridge
|
||||
false, // no first layer
|
||||
-1, // auto width
|
||||
*layerm.layer()->object()
|
||||
);
|
||||
f->spacing = internal_flow.spacing();
|
||||
using_internal_flow = true;
|
||||
} else {
|
||||
f->spacing = flow.spacing();
|
||||
}
|
||||
|
||||
double link_max_length = 0.;
|
||||
if (! is_bridge) {
|
||||
#if 0
|
||||
link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
|
||||
// printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
|
||||
#else
|
||||
if (density > 80.) // 80%
|
||||
link_max_length = 3. * f->spacing;
|
||||
#endif
|
||||
}
|
||||
|
||||
f->layer_id = layerm.layer()->id();
|
||||
f->z = layerm.layer()->print_z;
|
||||
f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
||||
// Maximum length of the perimeter segment linking two infill lines.
|
||||
f->link_max_length = scale_(link_max_length);
|
||||
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
|
||||
f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
|
||||
// f->layer_height = h;
|
||||
|
||||
// apply half spacing using this flow's own spacing and generate infill
|
||||
FillParams params;
|
||||
params.density = 0.01 * density;
|
||||
// params.dont_adjust = true;
|
||||
params.dont_adjust = false;
|
||||
Polylines polylines = f->fill_surface(&surface, params);
|
||||
if (polylines.empty())
|
||||
continue;
|
||||
|
||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||
// pattern generator)
|
||||
if (using_internal_flow) {
|
||||
// if we used the internal flow we're not doing a solid infill
|
||||
// so we can safely ignore the slight variation that might have
|
||||
// been applied to $f->flow_spacing
|
||||
} else {
|
||||
flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
|
||||
}
|
||||
|
||||
// Save into layer.
|
||||
auto *eec = new ExtrusionEntityCollection();
|
||||
out.entities.push_back(eec);
|
||||
// Only concentric fills are not sorted.
|
||||
eec->no_sort = f->no_sort();
|
||||
extrusion_entities_append_paths(
|
||||
eec->entities, STDMOVE(polylines),
|
||||
is_bridge ?
|
||||
erBridgeInfill :
|
||||
(surface.is_solid() ?
|
||||
((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
|
||||
erInternalInfill),
|
||||
flow.mm3_per_mm(), flow.width, flow.height);
|
||||
}
|
||||
|
||||
// add thin fill regions
|
||||
// thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
|
||||
// Unpacks the collection, creates multiple collections per path.
|
||||
// The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
|
||||
// Why the paths are unpacked?
|
||||
for (const ExtrusionEntity *thin_fill : layerm.thin_fills.entities) {
|
||||
ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
|
||||
out.entities.push_back(&collection);
|
||||
collection.entities.push_back(thin_fill->clone());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
36
src/libslic3r/Fill/Fill.hpp
Normal file
36
src/libslic3r/Fill/Fill.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef slic3r_Fill_hpp_
|
||||
#define slic3r_Fill_hpp_
|
||||
|
||||
#include <memory.h>
|
||||
#include <float.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ExtrusionEntityCollection;
|
||||
class LayerRegion;
|
||||
|
||||
// An interface class to Perl, aggregating an instance of a Fill and a FillData.
|
||||
class Filler
|
||||
{
|
||||
public:
|
||||
Filler() : fill(NULL) {}
|
||||
~Filler() {
|
||||
delete fill;
|
||||
fill = NULL;
|
||||
}
|
||||
Fill *fill;
|
||||
FillParams params;
|
||||
};
|
||||
|
||||
void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_Fill_hpp_
|
204
src/libslic3r/Fill/Fill3DHoneycomb.cpp
Normal file
204
src/libslic3r/Fill/Fill3DHoneycomb.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "Fill3DHoneycomb.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
/*
|
||||
Creates a contiguous sequence of points at a specified height that make
|
||||
up a horizontal slice of the edges of a space filling truncated
|
||||
octahedron tesselation. The octahedrons are oriented so that the
|
||||
square faces are in the horizontal plane with edges parallel to the X
|
||||
and Y axes.
|
||||
|
||||
Credits: David Eccles (gringer).
|
||||
*/
|
||||
|
||||
// Generate an array of points that are in the same direction as the
|
||||
// basic printing line (i.e. Y points for columns, X points for rows)
|
||||
// Note: a negative offset only causes a change in the perpendicular
|
||||
// direction
|
||||
static std::vector<coordf_t> colinearPoints(const coordf_t offset, const size_t baseLocation, size_t gridLength)
|
||||
{
|
||||
const coordf_t offset2 = std::abs(offset / coordf_t(2.));
|
||||
std::vector<coordf_t> points;
|
||||
points.push_back(baseLocation - offset2);
|
||||
for (size_t i = 0; i < gridLength; ++i) {
|
||||
points.push_back(baseLocation + i + offset2);
|
||||
points.push_back(baseLocation + i + 1 - offset2);
|
||||
}
|
||||
points.push_back(baseLocation + gridLength + offset2);
|
||||
return points;
|
||||
}
|
||||
|
||||
// Generate an array of points for the dimension that is perpendicular to
|
||||
// the basic printing line (i.e. X points for columns, Y points for rows)
|
||||
static std::vector<coordf_t> perpendPoints(const coordf_t offset, const size_t baseLocation, size_t gridLength)
|
||||
{
|
||||
coordf_t offset2 = offset / coordf_t(2.);
|
||||
coord_t side = 2 * (baseLocation & 1) - 1;
|
||||
std::vector<coordf_t> points;
|
||||
points.push_back(baseLocation - offset2 * side);
|
||||
for (size_t i = 0; i < gridLength; ++i) {
|
||||
side = 2*((i+baseLocation) & 1) - 1;
|
||||
points.push_back(baseLocation + offset2 * side);
|
||||
points.push_back(baseLocation + offset2 * side);
|
||||
}
|
||||
points.push_back(baseLocation - offset2 * side);
|
||||
return points;
|
||||
}
|
||||
|
||||
// Trims an array of points to specified rectangular limits. Point
|
||||
// components that are outside these limits are set to the limits.
|
||||
static inline void trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY)
|
||||
{
|
||||
for (Vec2d &pt : pts) {
|
||||
pt(0) = clamp(minX, maxX, pt(0));
|
||||
pt(1) = clamp(minY, maxY, pt(1));
|
||||
}
|
||||
}
|
||||
|
||||
static inline Pointfs zip(const std::vector<coordf_t> &x, const std::vector<coordf_t> &y)
|
||||
{
|
||||
assert(x.size() == y.size());
|
||||
Pointfs out;
|
||||
out.reserve(x.size());
|
||||
for (size_t i = 0; i < x.size(); ++ i)
|
||||
out.push_back(Vec2d(x[i], y[i]));
|
||||
return out;
|
||||
}
|
||||
|
||||
// Generate a set of curves (array of array of 2d points) that describe a
|
||||
// horizontal slice of a truncated regular octahedron with edge length 1.
|
||||
// curveType specifies which lines to print, 1 for vertical lines
|
||||
// (columns), 2 for horizontal lines (rows), and 3 for both.
|
||||
static std::vector<Pointfs> makeNormalisedGrid(coordf_t z, size_t gridWidth, size_t gridHeight, size_t curveType)
|
||||
{
|
||||
// offset required to create a regular octagram
|
||||
coordf_t octagramGap = coordf_t(0.5);
|
||||
|
||||
// sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap]
|
||||
coordf_t a = std::sqrt(coordf_t(2.)); // period
|
||||
coordf_t wave = fabs(fmod(z, a) - a/2.)/a*4. - 1.;
|
||||
coordf_t offset = wave * octagramGap;
|
||||
|
||||
std::vector<Pointfs> points;
|
||||
if ((curveType & 1) != 0) {
|
||||
for (size_t x = 0; x <= gridWidth; ++x) {
|
||||
points.push_back(Pointfs());
|
||||
Pointfs &newPoints = points.back();
|
||||
newPoints = zip(
|
||||
perpendPoints(offset, x, gridHeight),
|
||||
colinearPoints(offset, 0, gridHeight));
|
||||
// trim points to grid edges
|
||||
trim(newPoints, coordf_t(0.), coordf_t(0.), coordf_t(gridWidth), coordf_t(gridHeight));
|
||||
if (x & 1)
|
||||
std::reverse(newPoints.begin(), newPoints.end());
|
||||
}
|
||||
}
|
||||
if ((curveType & 2) != 0) {
|
||||
for (size_t y = 0; y <= gridHeight; ++y) {
|
||||
points.push_back(Pointfs());
|
||||
Pointfs &newPoints = points.back();
|
||||
newPoints = zip(
|
||||
colinearPoints(offset, 0, gridWidth),
|
||||
perpendPoints(offset, y, gridWidth));
|
||||
// trim points to grid edges
|
||||
trim(newPoints, coordf_t(0.), coordf_t(0.), coordf_t(gridWidth), coordf_t(gridHeight));
|
||||
if (y & 1)
|
||||
std::reverse(newPoints.begin(), newPoints.end());
|
||||
}
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
// Generate a set of curves (array of array of 2d points) that describe a
|
||||
// horizontal slice of a truncated regular octahedron with a specified
|
||||
// grid square size.
|
||||
static Polylines makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t gridHeight, size_t curveType)
|
||||
{
|
||||
coord_t scaleFactor = gridSize;
|
||||
coordf_t normalisedZ = coordf_t(z) / coordf_t(scaleFactor);
|
||||
std::vector<Pointfs> polylines = makeNormalisedGrid(normalisedZ, gridWidth, gridHeight, curveType);
|
||||
Polylines result;
|
||||
result.reserve(polylines.size());
|
||||
for (std::vector<Pointfs>::const_iterator it_polylines = polylines.begin(); it_polylines != polylines.end(); ++ it_polylines) {
|
||||
result.push_back(Polyline());
|
||||
Polyline &polyline = result.back();
|
||||
for (Pointfs::const_iterator it = it_polylines->begin(); it != it_polylines->end(); ++ it)
|
||||
polyline.points.push_back(Point(coord_t((*it)(0) * scaleFactor), coord_t((*it)(1) * scaleFactor)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Fill3DHoneycomb::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
BoundingBox bb = expolygon.contour.bounding_box();
|
||||
coord_t distance = coord_t(scale_(this->spacing) / params.density);
|
||||
|
||||
// align bounding box to a multiple of our honeycomb grid module
|
||||
// (a module is 2*$distance since one $distance half-module is
|
||||
// growing while the other $distance half-module is shrinking)
|
||||
bb.merge(_align_to_grid(bb.min, Point(2*distance, 2*distance)));
|
||||
|
||||
// generate pattern
|
||||
Polylines polylines = makeGrid(
|
||||
scale_(this->z),
|
||||
distance,
|
||||
ceil(bb.size()(0) / distance) + 1,
|
||||
ceil(bb.size()(1) / distance) + 1,
|
||||
((this->layer_id/thickness_layers) % 2) + 1);
|
||||
|
||||
// move pattern in place
|
||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it)
|
||||
it->translate(bb.min(0), bb.min(1));
|
||||
|
||||
// clip pattern to boundaries
|
||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON);
|
||||
if (! expolygons_off.empty()) {
|
||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
||||
assert(expolygons_off.size() == 1);
|
||||
std::swap(expolygon_off, expolygons_off.front());
|
||||
}
|
||||
}
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
std::move(polylines),
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
bool first = true;
|
||||
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
const Point &first_point = it_polyline->points.front();
|
||||
const Point &last_point = pts_end.back();
|
||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
||||
// connecting paths on the boundaries of internal regions
|
||||
if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance &&
|
||||
expolygon_off.contains(Line(last_point, first_point))) {
|
||||
// Append the polyline.
|
||||
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
polylines_out.emplace_back(std::move(*it_polyline));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
32
src/libslic3r/Fill/Fill3DHoneycomb.hpp
Normal file
32
src/libslic3r/Fill/Fill3DHoneycomb.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef slic3r_Fill3DHoneycomb_hpp_
|
||||
#define slic3r_Fill3DHoneycomb_hpp_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Fill3DHoneycomb : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new Fill3DHoneycomb(*this); };
|
||||
virtual ~Fill3DHoneycomb() {}
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
virtual bool use_bridge_flow() const { return true; }
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_Fill3DHoneycomb_hpp_
|
133
src/libslic3r/Fill/FillBase.cpp
Normal file
133
src/libslic3r/Fill/FillBase.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../Surface.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
#include "FillConcentric.hpp"
|
||||
#include "FillHoneycomb.hpp"
|
||||
#include "Fill3DHoneycomb.hpp"
|
||||
#include "FillGyroid.hpp"
|
||||
#include "FillPlanePath.hpp"
|
||||
#include "FillRectilinear.hpp"
|
||||
#include "FillRectilinear2.hpp"
|
||||
#include "FillRectilinear3.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Fill* Fill::new_from_type(const InfillPattern type)
|
||||
{
|
||||
switch (type) {
|
||||
case ipConcentric: return new FillConcentric();
|
||||
case ipHoneycomb: return new FillHoneycomb();
|
||||
case ip3DHoneycomb: return new Fill3DHoneycomb();
|
||||
case ipGyroid: return new FillGyroid();
|
||||
case ipRectilinear: return new FillRectilinear2();
|
||||
// case ipRectilinear: return new FillRectilinear();
|
||||
case ipLine: return new FillLine();
|
||||
case ipGrid: return new FillGrid2();
|
||||
case ipTriangles: return new FillTriangles();
|
||||
case ipStars: return new FillStars();
|
||||
case ipCubic: return new FillCubic();
|
||||
// case ipGrid: return new FillGrid();
|
||||
case ipArchimedeanChords: return new FillArchimedeanChords();
|
||||
case ipHilbertCurve: return new FillHilbertCurve();
|
||||
case ipOctagramSpiral: return new FillOctagramSpiral();
|
||||
default: throw std::invalid_argument("unknown type");;
|
||||
}
|
||||
}
|
||||
|
||||
Fill* Fill::new_from_type(const std::string &type)
|
||||
{
|
||||
const t_config_enum_values &enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
t_config_enum_values::const_iterator it = enum_keys_map.find(type);
|
||||
return (it == enum_keys_map.end()) ? nullptr : new_from_type(InfillPattern(it->second));
|
||||
}
|
||||
|
||||
Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
// Perform offset.
|
||||
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing)));
|
||||
// Create the infills for each of the regions.
|
||||
Polylines polylines_out;
|
||||
for (size_t i = 0; i < expp.size(); ++ i)
|
||||
_fill_surface_single(
|
||||
params,
|
||||
surface->thickness_layers,
|
||||
_infill_direction(surface),
|
||||
expp[i],
|
||||
polylines_out);
|
||||
return polylines_out;
|
||||
}
|
||||
|
||||
// Calculate a new spacing to fill width with possibly integer number of lines,
|
||||
// the first and last line being centered at the interval ends.
|
||||
// This function possibly increases the spacing, never decreases,
|
||||
// and for a narrow width the increase in spacing may become severe,
|
||||
// therefore the adjustment is limited to 20% increase.
|
||||
coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
|
||||
{
|
||||
assert(width >= 0);
|
||||
assert(distance > 0);
|
||||
// floor(width / distance)
|
||||
coord_t number_of_intervals = (width - EPSILON) / distance;
|
||||
coord_t distance_new = (number_of_intervals == 0) ?
|
||||
distance :
|
||||
((width - EPSILON) / number_of_intervals);
|
||||
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
|
||||
assert(factor > 1. - 1e-5);
|
||||
// How much could the extrusion width be increased? By 20%.
|
||||
const coordf_t factor_max = 1.2;
|
||||
if (factor > factor_max)
|
||||
distance_new = coord_t(floor((coordf_t(distance) * factor_max + 0.5)));
|
||||
return distance_new;
|
||||
}
|
||||
|
||||
// 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.
|
||||
std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
|
||||
{
|
||||
// set infill angle
|
||||
float out_angle = this->angle;
|
||||
|
||||
if (out_angle == FLT_MAX) {
|
||||
//FIXME Vojtech: Add a warning?
|
||||
printf("Using undefined infill angle\n");
|
||||
out_angle = 0.f;
|
||||
}
|
||||
|
||||
// Bounding box is the bounding box of a perl object Slic3r::Print::Object (c++ object Slic3r::PrintObject)
|
||||
// The bounding box is only undefined in unit tests.
|
||||
Point out_shift = empty(this->bounding_box) ?
|
||||
surface->expolygon.contour.bounding_box().center() :
|
||||
this->bounding_box.center();
|
||||
|
||||
#if 0
|
||||
if (empty(this->bounding_box)) {
|
||||
printf("Fill::_infill_direction: empty bounding box!");
|
||||
} else {
|
||||
printf("Fill::_infill_direction: reference point %d, %d\n", out_shift.x, out_shift.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (surface->bridge_angle >= 0) {
|
||||
// use bridge angle
|
||||
//FIXME Vojtech: Add a debugf?
|
||||
// Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("Filling bridge with angle %f\n", surface->bridge_angle);
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
out_angle = surface->bridge_angle;
|
||||
} else if (this->layer_id != size_t(-1)) {
|
||||
// alternate fill direction
|
||||
out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers);
|
||||
} else {
|
||||
// printf("Layer_ID undefined!\n");
|
||||
}
|
||||
|
||||
out_angle += float(M_PI/2.);
|
||||
return std::pair<float, Point>(out_angle, out_shift);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
133
src/libslic3r/Fill/FillBase.hpp
Normal file
133
src/libslic3r/Fill/FillBase.hpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
#ifndef slic3r_FillBase_hpp_
|
||||
#define slic3r_FillBase_hpp_
|
||||
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include <float.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
struct FillParams
|
||||
{
|
||||
FillParams() {
|
||||
memset(this, 0, sizeof(FillParams));
|
||||
// Adjustment does not work.
|
||||
dont_adjust = true;
|
||||
}
|
||||
|
||||
bool full_infill() const { return density > 0.9999f; }
|
||||
|
||||
// Fill density, fraction in <0, 1>
|
||||
float density;
|
||||
|
||||
// Don't connect the fill lines around the inner perimeter.
|
||||
bool dont_connect;
|
||||
|
||||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust;
|
||||
|
||||
// For Honeycomb.
|
||||
// we were requested to complete each loop;
|
||||
// in this case we don't try to make more continuous paths
|
||||
bool complete;
|
||||
};
|
||||
|
||||
class Fill
|
||||
{
|
||||
public:
|
||||
// Index of the layer.
|
||||
size_t layer_id;
|
||||
// Z coordinate of the top print surface, in unscaled coordinates
|
||||
coordf_t z;
|
||||
// in unscaled coordinates
|
||||
coordf_t spacing;
|
||||
// infill / perimeter overlap, in unscaled coordinates
|
||||
coordf_t overlap;
|
||||
// in radians, ccw, 0 = East
|
||||
float angle;
|
||||
// In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines.
|
||||
// Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic.
|
||||
// If left to zero, the links will not be limited.
|
||||
coord_t link_max_length;
|
||||
// In scaled coordinates. Used by the concentric infill pattern to clip the loops to create extrusion paths.
|
||||
coord_t loop_clipping;
|
||||
// In scaled coordinates. Bounding box of the 2D projection of the object.
|
||||
BoundingBox bounding_box;
|
||||
|
||||
public:
|
||||
virtual ~Fill() {}
|
||||
|
||||
static Fill* new_from_type(const InfillPattern type);
|
||||
static Fill* new_from_type(const std::string &type);
|
||||
|
||||
void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; }
|
||||
|
||||
// Use bridge flow for the fill?
|
||||
virtual bool use_bridge_flow() const { return false; }
|
||||
|
||||
// Do not sort the fill lines to optimize the print head path?
|
||||
virtual bool no_sort() const { return false; }
|
||||
|
||||
// Perform the fill.
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
Fill() :
|
||||
layer_id(size_t(-1)),
|
||||
z(0.),
|
||||
spacing(0.),
|
||||
// Infill / perimeter overlap.
|
||||
overlap(0.),
|
||||
// Initial angle is undefined.
|
||||
angle(FLT_MAX),
|
||||
link_max_length(0),
|
||||
loop_clipping(0),
|
||||
// The initial bounding box is empty, therefore undefined.
|
||||
bounding_box(Point(0, 0), Point(-1, -1))
|
||||
{}
|
||||
|
||||
// The expolygon may be modified by the method to avoid a copy.
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams & /* params */,
|
||||
unsigned int /* thickness_layers */,
|
||||
const std::pair<float, Point> & /* direction */,
|
||||
ExPolygon & /* expolygon */,
|
||||
Polylines & /* polylines_out */) {};
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; }
|
||||
|
||||
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;
|
||||
|
||||
public:
|
||||
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
|
||||
|
||||
// Align a coordinate to a grid. The coordinate may be negative,
|
||||
// the aligned value will never be bigger than the original one.
|
||||
static coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
|
||||
// Current C++ standard defines the result of integer division to be rounded to zero,
|
||||
// for both positive and negative numbers. Here we want to round down for negative
|
||||
// numbers as well.
|
||||
coord_t aligned = (coord < 0) ?
|
||||
((coord - spacing + 1) / spacing) * spacing :
|
||||
(coord / spacing) * spacing;
|
||||
assert(aligned <= coord);
|
||||
return aligned;
|
||||
}
|
||||
static Point _align_to_grid(Point coord, Point spacing)
|
||||
{ return Point(_align_to_grid(coord(0), spacing(0)), _align_to_grid(coord(1), spacing(1))); }
|
||||
static coord_t _align_to_grid(coord_t coord, coord_t spacing, coord_t base)
|
||||
{ return base + _align_to_grid(coord - base, spacing); }
|
||||
static Point _align_to_grid(Point coord, Point spacing, Point base)
|
||||
{ return Point(_align_to_grid(coord(0), spacing(0), base(0)), _align_to_grid(coord(1), spacing(1), base(1))); }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillBase_hpp_
|
64
src/libslic3r/Fill/FillConcentric.cpp
Normal file
64
src/libslic3r/Fill/FillConcentric.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillConcentric.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void FillConcentric::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
|
||||
coord_t min_spacing = scale_(this->spacing);
|
||||
coord_t distance = coord_t(min_spacing / params.density);
|
||||
|
||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||
distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance);
|
||||
this->spacing = unscale<double>(distance);
|
||||
}
|
||||
|
||||
Polygons loops = (Polygons)expolygon;
|
||||
Polygons last = loops;
|
||||
while (! last.empty()) {
|
||||
last = offset2(last, -(distance + min_spacing/2), +min_spacing/2);
|
||||
loops.insert(loops.end(), last.begin(), last.end());
|
||||
}
|
||||
|
||||
// generate paths from the outermost to the innermost, to avoid
|
||||
// adhesion problems of the first central tiny loops
|
||||
loops = union_pt_chained(loops, false);
|
||||
|
||||
// split paths using a nearest neighbor search
|
||||
size_t iPathFirst = polylines_out.size();
|
||||
Point last_pos(0, 0);
|
||||
for (const Polygon &loop : loops) {
|
||||
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop)));
|
||||
last_pos = polylines_out.back().last_point();
|
||||
}
|
||||
|
||||
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
|
||||
// Keep valid paths only.
|
||||
size_t j = iPathFirst;
|
||||
for (size_t i = iPathFirst; i < polylines_out.size(); ++ i) {
|
||||
polylines_out[i].clip_end(this->loop_clipping);
|
||||
if (polylines_out[i].is_valid()) {
|
||||
if (j < i)
|
||||
polylines_out[j] = std::move(polylines_out[i]);
|
||||
++ j;
|
||||
}
|
||||
}
|
||||
if (j < polylines_out.size())
|
||||
polylines_out.erase(polylines_out.begin() + j, polylines_out.end());
|
||||
//TODO: return ExtrusionLoop objects to get better chained paths,
|
||||
// otherwise the outermost loop starts at the closest point to (0, 0).
|
||||
// We want the loops to be split inside the G-code generator to get optimum path planning.
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
27
src/libslic3r/Fill/FillConcentric.hpp
Normal file
27
src/libslic3r/Fill/FillConcentric.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef slic3r_FillConcentric_hpp_
|
||||
#define slic3r_FillConcentric_hpp_
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class FillConcentric : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillConcentric() {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillConcentric(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
|
||||
virtual bool no_sort() const { return true; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillConcentric_hpp_
|
196
src/libslic3r/Fill/FillGyroid.cpp
Normal file
196
src/libslic3r/Fill/FillGyroid.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "FillGyroid.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
|
||||
{
|
||||
if (vertical) {
|
||||
double phase_offset = (z_cos < 0 ? M_PI : 0) + M_PI;
|
||||
double a = sin(x + phase_offset);
|
||||
double b = - z_cos;
|
||||
double res = z_sin * cos(x + phase_offset + (flip ? M_PI : 0.));
|
||||
double r = sqrt(sqr(a) + sqr(b));
|
||||
return asin(a/r) + asin(res/r) + M_PI;
|
||||
}
|
||||
else {
|
||||
double phase_offset = z_sin < 0 ? M_PI : 0.;
|
||||
double a = cos(x + phase_offset);
|
||||
double b = - z_sin;
|
||||
double res = z_cos * sin(x + phase_offset + (flip ? 0 : M_PI));
|
||||
double r = sqrt(sqr(a) + sqr(b));
|
||||
return (asin(a/r) + asin(res/r) + 0.5 * M_PI);
|
||||
}
|
||||
}
|
||||
|
||||
static inline Polyline make_wave(
|
||||
const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
|
||||
double z_cos, double z_sin, bool vertical)
|
||||
{
|
||||
std::vector<Vec2d> points = one_period;
|
||||
double period = points.back()(0);
|
||||
points.pop_back();
|
||||
int n = points.size();
|
||||
do {
|
||||
points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1)));
|
||||
} while (points.back()(0) < width);
|
||||
points.back()(0) = width;
|
||||
|
||||
// and construct the final polyline to return:
|
||||
Polyline polyline;
|
||||
for (auto& point : points) {
|
||||
point(1) += offset;
|
||||
point(1) = clamp(0., height, double(point(1)));
|
||||
if (vertical)
|
||||
std::swap(point(0), point(1));
|
||||
polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
|
||||
}
|
||||
|
||||
return polyline;
|
||||
}
|
||||
|
||||
static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip)
|
||||
{
|
||||
std::vector<Vec2d> points;
|
||||
double dx = M_PI_4; // very coarse spacing to begin with
|
||||
double limit = std::min(2*M_PI, width);
|
||||
for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too
|
||||
x = std::min(x, limit);
|
||||
points.emplace_back(Vec2d(x,f(x, z_sin,z_cos, vertical, flip)));
|
||||
}
|
||||
|
||||
// now we will check all internal points and in case some are too far from the line connecting its neighbours,
|
||||
// we will add one more point on each side:
|
||||
const double tolerance = .1;
|
||||
for (unsigned int i=1;i<points.size()-1;++i) {
|
||||
auto& lp = points[i-1]; // left point
|
||||
auto& tp = points[i]; // this point
|
||||
Vec2d lrv = tp - lp;
|
||||
auto& rp = points[i+1]; // right point
|
||||
// calculate distance of the point to the line:
|
||||
double dist_mm = unscale<double>(scaleFactor) * std::abs(cross2(rp, lp) - cross2(rp - lp, tp)) / lrv.norm();
|
||||
if (dist_mm > tolerance) { // if the difference from straight line is more than this
|
||||
double x = 0.5f * (points[i-1](0) + points[i](0));
|
||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
||||
x = 0.5f * (points[i+1](0) + points[i](0));
|
||||
points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
|
||||
// we added the points to the end, but need them all in order
|
||||
std::sort(points.begin(), points.end(), [](const Vec2d &lhs, const Vec2d &rhs){ return lhs < rhs; });
|
||||
// decrement i so we also check the first newly added point
|
||||
--i;
|
||||
}
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
|
||||
{
|
||||
const double scaleFactor = scale_(line_spacing) / density_adjusted;
|
||||
//scale factor for 5% : 8 712 388
|
||||
// 1z = 10^-6 mm ?
|
||||
const double z = gridZ / scaleFactor;
|
||||
const double z_sin = sin(z);
|
||||
const double z_cos = cos(z);
|
||||
|
||||
bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
|
||||
double lower_bound = 0.;
|
||||
double upper_bound = height;
|
||||
bool flip = true;
|
||||
if (vertical) {
|
||||
flip = false;
|
||||
lower_bound = -M_PI;
|
||||
upper_bound = width - M_PI_2;
|
||||
std::swap(width,height);
|
||||
}
|
||||
|
||||
std::vector<Vec2d> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time
|
||||
Polylines result;
|
||||
|
||||
for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines
|
||||
result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
|
||||
|
||||
flip = !flip; // even polylines are a bit shifted
|
||||
one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample
|
||||
for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates even polylines
|
||||
result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void FillGyroid::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// no rotation is supported for this infill pattern (yet)
|
||||
BoundingBox bb = expolygon.contour.bounding_box();
|
||||
// Density adjusted to have a good %of weight.
|
||||
double density_adjusted = std::max(0., params.density * 2.);
|
||||
// Distance between the gyroid waves in scaled coordinates.
|
||||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||
|
||||
// align bounding box to a multiple of our grid module
|
||||
bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance)));
|
||||
|
||||
// generate pattern
|
||||
Polylines polylines = make_gyroid_waves(
|
||||
scale_(this->z),
|
||||
density_adjusted,
|
||||
this->spacing,
|
||||
ceil(bb.size()(0) / distance) + 1.,
|
||||
ceil(bb.size()(1) / distance) + 1.);
|
||||
|
||||
// move pattern in place
|
||||
for (Polyline &polyline : polylines)
|
||||
polyline.translate(bb.min(0), bb.min(1));
|
||||
|
||||
// clip pattern to boundaries
|
||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
ExPolygon expolygon_off;
|
||||
{
|
||||
ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON);
|
||||
if (! expolygons_off.empty()) {
|
||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
||||
assert(expolygons_off.size() == 1);
|
||||
std::swap(expolygon_off, expolygons_off.front());
|
||||
}
|
||||
}
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
std::move(polylines),
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
bool first = true;
|
||||
for (Polyline &polyline : chained) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
const Point &first_point = polyline.points.front();
|
||||
const Point &last_point = pts_end.back();
|
||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
||||
// connecting paths on the boundaries of internal regions
|
||||
// TODO: avoid crossing current infill path
|
||||
if ((last_point - first_point).cast<double>().norm() <= 5 * distance &&
|
||||
expolygon_off.contains(Line(last_point, first_point))) {
|
||||
// Append the polyline.
|
||||
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
polylines_out.emplace_back(std::move(polyline));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
30
src/libslic3r/Fill/FillGyroid.hpp
Normal file
30
src/libslic3r/Fill/FillGyroid.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef slic3r_FillGyroid_hpp_
|
||||
#define slic3r_FillGyroid_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class FillGyroid : public Fill
|
||||
{
|
||||
public:
|
||||
FillGyroid() {}
|
||||
virtual Fill* clone() const { return new FillGyroid(*this); }
|
||||
|
||||
// require bridge flow since most of this pattern hangs in air
|
||||
virtual bool use_bridge_flow() const { return true; }
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillGyroid_hpp_
|
125
src/libslic3r/Fill/FillHoneycomb.cpp
Normal file
125
src/libslic3r/Fill/FillHoneycomb.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillHoneycomb.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void FillHoneycomb::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// cache hexagons math
|
||||
CacheID cache_id(params.density, this->spacing);
|
||||
Cache::iterator it_m = this->cache.find(cache_id);
|
||||
if (it_m == this->cache.end()) {
|
||||
it_m = this->cache.insert(it_m, std::pair<CacheID, CacheData>(cache_id, CacheData()));
|
||||
CacheData &m = it_m->second;
|
||||
coord_t min_spacing = scale_(this->spacing);
|
||||
m.distance = min_spacing / params.density;
|
||||
m.hex_side = m.distance / (sqrt(3)/2);
|
||||
m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3);
|
||||
coord_t hex_height = m.hex_side * 2;
|
||||
m.pattern_height = hex_height + m.hex_side;
|
||||
m.y_short = m.distance * sqrt(3)/3;
|
||||
m.x_offset = min_spacing / 2;
|
||||
m.y_offset = m.x_offset * sqrt(3)/3;
|
||||
m.hex_center = Point(m.hex_width/2, m.hex_side);
|
||||
}
|
||||
CacheData &m = it_m->second;
|
||||
|
||||
Polygons polygons;
|
||||
{
|
||||
// adjust actual bounding box to the nearest multiple of our hex pattern
|
||||
// and align it so that it matches across layers
|
||||
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
{
|
||||
// rotate bounding box according to infill direction
|
||||
Polygon bb_polygon = bounding_box.polygon();
|
||||
bb_polygon.rotate(direction.first, m.hex_center);
|
||||
bounding_box = bb_polygon.bounding_box();
|
||||
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one
|
||||
// The infill is not aligned to the object bounding box, but to a world coordinate system. Supposedly good enough.
|
||||
bounding_box.merge(_align_to_grid(bounding_box.min, Point(m.hex_width, m.pattern_height)));
|
||||
}
|
||||
|
||||
coord_t x = bounding_box.min(0);
|
||||
while (x <= bounding_box.max(0)) {
|
||||
Polygon p;
|
||||
coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset };
|
||||
for (size_t i = 0; i < 2; ++ i) {
|
||||
std::reverse(p.points.begin(), p.points.end()); // turn first half upside down
|
||||
for (coord_t y = bounding_box.min(1); y <= bounding_box.max(1); y += m.y_short + m.hex_side + m.y_short + m.hex_side) {
|
||||
p.points.push_back(Point(ax[1], y + m.y_offset));
|
||||
p.points.push_back(Point(ax[0], y + m.y_short - m.y_offset));
|
||||
p.points.push_back(Point(ax[0], y + m.y_short + m.hex_side + m.y_offset));
|
||||
p.points.push_back(Point(ax[1], y + m.y_short + m.hex_side + m.y_short - m.y_offset));
|
||||
p.points.push_back(Point(ax[1], y + m.y_short + m.hex_side + m.y_short + m.hex_side + m.y_offset));
|
||||
}
|
||||
ax[0] = ax[0] + m.distance;
|
||||
ax[1] = ax[1] + m.distance;
|
||||
std::swap(ax[0], ax[1]); // draw symmetrical pattern
|
||||
x += m.distance;
|
||||
}
|
||||
p.rotate(-direction.first, m.hex_center);
|
||||
polygons.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.complete || true) {
|
||||
// we were requested to complete each loop;
|
||||
// in this case we don't try to make more continuous paths
|
||||
Polygons polygons_trimmed = intersection((Polygons)expolygon, polygons);
|
||||
for (Polygons::iterator it = polygons_trimmed.begin(); it != polygons_trimmed.end(); ++ it)
|
||||
polylines_out.push_back(it->split_at_first_point());
|
||||
} else {
|
||||
// consider polygons as polylines without re-appending the initial point:
|
||||
// this cuts the last segment on purpose, so that the jump to the next
|
||||
// path is more straight
|
||||
Polylines paths;
|
||||
{
|
||||
Polylines p;
|
||||
for (Polygon &poly : polygons)
|
||||
p.emplace_back(poly.points);
|
||||
paths = intersection_pl(p, to_polygons(expolygon));
|
||||
}
|
||||
|
||||
// connect paths
|
||||
if (! paths.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
std::move(paths),
|
||||
PolylineCollection::leftmost_point(paths), false);
|
||||
assert(paths.empty());
|
||||
paths.clear();
|
||||
for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) {
|
||||
if (! paths.empty()) {
|
||||
// distance between first point of this path and last point of last path
|
||||
double distance = (it_path->first_point() - paths.back().last_point()).cast<double>().norm();
|
||||
if (distance <= m.hex_width) {
|
||||
paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Don't connect the paths.
|
||||
paths.push_back(*it_path);
|
||||
}
|
||||
}
|
||||
|
||||
// clip paths again to prevent connection segments from crossing the expolygon boundaries
|
||||
paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
|
||||
// Move the polylines to the output, avoid a deep copy.
|
||||
size_t j = polylines_out.size();
|
||||
polylines_out.resize(j + paths.size(), Polyline());
|
||||
for (size_t i = 0; i < paths.size(); ++ i)
|
||||
std::swap(polylines_out[j ++], paths[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
57
src/libslic3r/Fill/FillHoneycomb.hpp
Normal file
57
src/libslic3r/Fill/FillHoneycomb.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#ifndef slic3r_FillHoneycomb_hpp_
|
||||
#define slic3r_FillHoneycomb_hpp_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class FillHoneycomb : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillHoneycomb() {}
|
||||
|
||||
protected:
|
||||
virtual Fill* clone() const { return new FillHoneycomb(*this); };
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
|
||||
// Caching the
|
||||
struct CacheID
|
||||
{
|
||||
CacheID(float adensity, coordf_t aspacing) :
|
||||
density(adensity), spacing(aspacing) {}
|
||||
float density;
|
||||
coordf_t spacing;
|
||||
bool operator<(const CacheID &other) const
|
||||
{ return (density < other.density) || (density == other.density && spacing < other.spacing); }
|
||||
bool operator==(const CacheID &other) const
|
||||
{ return density == other.density && spacing == other.spacing; }
|
||||
};
|
||||
struct CacheData
|
||||
{
|
||||
coord_t distance;
|
||||
coord_t hex_side;
|
||||
coord_t hex_width;
|
||||
coord_t pattern_height;
|
||||
coord_t y_short;
|
||||
coord_t x_offset;
|
||||
coord_t y_offset;
|
||||
Point hex_center;
|
||||
};
|
||||
typedef std::map<CacheID, CacheData> Cache;
|
||||
Cache cache;
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return float(M_PI/3.) * (idx % 3); }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillHoneycomb_hpp_
|
203
src/libslic3r/Fill/FillPlanePath.cpp
Normal file
203
src/libslic3r/Fill/FillPlanePath.cpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillPlanePath.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void FillPlanePath::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
expolygon.rotate(- direction.first);
|
||||
|
||||
coord_t distance_between_lines = scale_(this->spacing) / params.density;
|
||||
|
||||
// align infill across layers using the object's bounding box
|
||||
// Rotated bounding box of the whole object.
|
||||
BoundingBox bounding_box = this->bounding_box.rotated(- direction.first);
|
||||
|
||||
Point shift = this->_centered() ?
|
||||
bounding_box.center() :
|
||||
bounding_box.min;
|
||||
expolygon.translate(-shift(0), -shift(1));
|
||||
bounding_box.translate(-shift(0), -shift(1));
|
||||
|
||||
Pointfs pts = _generate(
|
||||
coord_t(ceil(coordf_t(bounding_box.min(0)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.min(1)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max(0)) / distance_between_lines)),
|
||||
coord_t(ceil(coordf_t(bounding_box.max(1)) / distance_between_lines)));
|
||||
|
||||
Polylines polylines;
|
||||
if (pts.size() >= 2) {
|
||||
// Convert points to a polyline, upscale.
|
||||
polylines.push_back(Polyline());
|
||||
Polyline &polyline = polylines.back();
|
||||
polyline.points.reserve(pts.size());
|
||||
for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it)
|
||||
polyline.points.push_back(Point(
|
||||
coord_t(floor((*it)(0) * distance_between_lines + 0.5)),
|
||||
coord_t(floor((*it)(1) * distance_between_lines + 0.5))));
|
||||
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
/*
|
||||
if (1) {
|
||||
require "Slic3r/SVG.pm";
|
||||
print "Writing fill.svg\n";
|
||||
Slic3r::SVG::output("fill.svg",
|
||||
no_arrows => 1,
|
||||
polygons => \@$expolygon,
|
||||
green_polygons => [ $bounding_box->polygon ],
|
||||
polylines => [ $polyline ],
|
||||
red_polylines => \@paths,
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
// paths must be repositioned and rotated back
|
||||
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
|
||||
it->translate(shift(0), shift(1));
|
||||
it->rotate(direction.first);
|
||||
}
|
||||
}
|
||||
|
||||
// Move the polylines to the output, avoid a deep copy.
|
||||
size_t j = polylines_out.size();
|
||||
polylines_out.resize(j + polylines.size(), Polyline());
|
||||
for (size_t i = 0; i < polylines.size(); ++ i)
|
||||
std::swap(polylines_out[j ++], polylines[i]);
|
||||
}
|
||||
|
||||
// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
|
||||
Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
|
||||
{
|
||||
// Radius to achieve.
|
||||
coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
|
||||
// Now unwind the spiral.
|
||||
coordf_t a = 1.;
|
||||
coordf_t b = 1./(2.*M_PI);
|
||||
coordf_t theta = 0.;
|
||||
coordf_t r = 1;
|
||||
Pointfs out;
|
||||
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
|
||||
out.push_back(Vec2d(0, 0));
|
||||
out.push_back(Vec2d(1, 0));
|
||||
while (r < rmax) {
|
||||
theta += 1. / r;
|
||||
r = a + b * theta;
|
||||
out.push_back(Vec2d(r * cos(theta), r * sin(theta)));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Adapted from
|
||||
// http://cpansearch.perl.org/src/KRYDE/Math-PlanePath-122/lib/Math/PlanePath/HilbertCurve.pm
|
||||
//
|
||||
// state=0 3--2 plain
|
||||
// |
|
||||
// 0--1
|
||||
//
|
||||
// state=4 1--2 transpose
|
||||
// | |
|
||||
// 0 3
|
||||
//
|
||||
// state=8
|
||||
//
|
||||
// state=12 3 0 rot180 + transpose
|
||||
// | |
|
||||
// 2--1
|
||||
//
|
||||
static inline Point hilbert_n_to_xy(const size_t n)
|
||||
{
|
||||
static const int next_state[16] = { 4,0,0,12, 0,4,4,8, 12,8,8,4, 8,12,12,0 };
|
||||
static const int digit_to_x[16] = { 0,1,1,0, 0,0,1,1, 1,0,0,1, 1,1,0,0 };
|
||||
static const int digit_to_y[16] = { 0,0,1,1, 0,1,1,0, 1,1,0,0, 1,0,0,1 };
|
||||
|
||||
// Number of 2 bit digits.
|
||||
size_t ndigits = 0;
|
||||
{
|
||||
size_t nc = n;
|
||||
while(nc > 0) {
|
||||
nc >>= 2;
|
||||
++ ndigits;
|
||||
}
|
||||
}
|
||||
int state = (ndigits & 1) ? 4 : 0;
|
||||
int dirstate = (ndigits & 1) ? 0 : 4;
|
||||
coord_t x = 0;
|
||||
coord_t y = 0;
|
||||
for (int i = (int)ndigits - 1; i >= 0; -- i) {
|
||||
int digit = (n >> (i * 2)) & 3;
|
||||
state += digit;
|
||||
if (digit != 3)
|
||||
dirstate = state; // lowest non-3 digit
|
||||
x |= digit_to_x[state] << i;
|
||||
y |= digit_to_y[state] << i;
|
||||
state = next_state[state];
|
||||
}
|
||||
return Point(x, y);
|
||||
}
|
||||
|
||||
Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
|
||||
{
|
||||
// Minimum power of two square to fit the domain.
|
||||
size_t sz = 2;
|
||||
size_t pw = 1;
|
||||
{
|
||||
size_t sz0 = std::max(max_x + 1 - min_x, max_y + 1 - min_y);
|
||||
while (sz < sz0) {
|
||||
sz = sz << 1;
|
||||
++ pw;
|
||||
}
|
||||
}
|
||||
|
||||
size_t sz2 = sz * sz;
|
||||
Pointfs line;
|
||||
line.reserve(sz2);
|
||||
for (size_t i = 0; i < sz2; ++ i) {
|
||||
Point p = hilbert_n_to_xy(i);
|
||||
line.push_back(Vec2d(p(0) + min_x, p(1) + min_y));
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y)
|
||||
{
|
||||
// Radius to achieve.
|
||||
coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
|
||||
// Now unwind the spiral.
|
||||
coordf_t r = 0;
|
||||
coordf_t r_inc = sqrt(2.);
|
||||
Pointfs out;
|
||||
out.push_back(Vec2d(0, 0));
|
||||
while (r < rmax) {
|
||||
r += r_inc;
|
||||
coordf_t rx = r / sqrt(2.);
|
||||
coordf_t r2 = r + rx;
|
||||
out.push_back(Vec2d( r, 0.));
|
||||
out.push_back(Vec2d( r2, rx));
|
||||
out.push_back(Vec2d( rx, rx));
|
||||
out.push_back(Vec2d( rx, r2));
|
||||
out.push_back(Vec2d(0., r));
|
||||
out.push_back(Vec2d(-rx, r2));
|
||||
out.push_back(Vec2d(-rx, rx));
|
||||
out.push_back(Vec2d(-r2, rx));
|
||||
out.push_back(Vec2d(-r, 0.));
|
||||
out.push_back(Vec2d(-r2, -rx));
|
||||
out.push_back(Vec2d(-rx, -rx));
|
||||
out.push_back(Vec2d(-rx, -r2));
|
||||
out.push_back(Vec2d(0., -r));
|
||||
out.push_back(Vec2d( rx, -r2));
|
||||
out.push_back(Vec2d( rx, -rx));
|
||||
out.push_back(Vec2d( r2+r_inc, -rx));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
69
src/libslic3r/Fill/FillPlanePath.hpp
Normal file
69
src/libslic3r/Fill/FillPlanePath.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#ifndef slic3r_FillPlanePath_hpp_
|
||||
#define slic3r_FillPlanePath_hpp_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// The original Perl code used path generators from Math::PlanePath library:
|
||||
// http://user42.tuxfamily.org/math-planepath/
|
||||
// http://user42.tuxfamily.org/math-planepath/gallery.html
|
||||
|
||||
class FillPlanePath : public Fill
|
||||
{
|
||||
public:
|
||||
virtual ~FillPlanePath() {}
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
|
||||
virtual float _layer_angle(size_t idx) const { return 0.f; }
|
||||
virtual bool _centered() const = 0;
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) = 0;
|
||||
};
|
||||
|
||||
class FillArchimedeanChords : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillArchimedeanChords(*this); };
|
||||
virtual ~FillArchimedeanChords() {}
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return true; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
};
|
||||
|
||||
class FillHilbertCurve : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillHilbertCurve(*this); };
|
||||
virtual ~FillHilbertCurve() {}
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return false; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
};
|
||||
|
||||
class FillOctagramSpiral : public FillPlanePath
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillOctagramSpiral(*this); };
|
||||
virtual ~FillOctagramSpiral() {}
|
||||
|
||||
protected:
|
||||
virtual bool _centered() const { return true; }
|
||||
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillPlanePath_hpp_
|
130
src/libslic3r/Fill/FillRectilinear.cpp
Normal file
130
src/libslic3r/Fill/FillRectilinear.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
#include "../PolylineCollection.hpp"
|
||||
#include "../Surface.hpp"
|
||||
|
||||
#include "FillRectilinear.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void FillRectilinear::_fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
// rotate polygons so that we can work with vertical lines here
|
||||
expolygon.rotate(- direction.first);
|
||||
|
||||
this->_min_spacing = scale_(this->spacing);
|
||||
assert(params.density > 0.0001f && params.density <= 1.f);
|
||||
this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density);
|
||||
this->_diagonal_distance = this->_line_spacing * 2;
|
||||
this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill
|
||||
BoundingBox bounding_box = expolygon.contour.bounding_box();
|
||||
|
||||
// define flow spacing according to requested density
|
||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||
this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing);
|
||||
this->spacing = unscale<double>(this->_line_spacing);
|
||||
} else {
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// Transform the reference point to the rotated coordinate system.
|
||||
bounding_box.merge(_align_to_grid(
|
||||
bounding_box.min,
|
||||
Point(this->_line_spacing, this->_line_spacing),
|
||||
direction.second.rotated(- direction.first)));
|
||||
}
|
||||
|
||||
// generate the basic pattern
|
||||
coord_t x_max = bounding_box.max(0) + SCALED_EPSILON;
|
||||
Lines lines;
|
||||
for (coord_t x = bounding_box.min(0); x <= x_max; x += this->_line_spacing)
|
||||
lines.push_back(this->_line(lines.size(), x, bounding_box.min(1), bounding_box.max(1)));
|
||||
if (this->_horizontal_lines()) {
|
||||
coord_t y_max = bounding_box.max(1) + SCALED_EPSILON;
|
||||
for (coord_t y = bounding_box.min(1); y <= y_max; y += this->_line_spacing)
|
||||
lines.push_back(Line(Point(bounding_box.min(0), y), Point(bounding_box.max(0), y)));
|
||||
}
|
||||
|
||||
// clip paths against a slightly larger expolygon, so that the first and last paths
|
||||
// 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
|
||||
//FIXME Vojtech: Update the intersecton function to work directly with lines.
|
||||
Polylines polylines_src;
|
||||
polylines_src.reserve(lines.size());
|
||||
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) {
|
||||
polylines_src.push_back(Polyline());
|
||||
Points &pts = polylines_src.back().points;
|
||||
pts.reserve(2);
|
||||
pts.push_back(it->a);
|
||||
pts.push_back(it->b);
|
||||
}
|
||||
Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(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?
|
||||
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 (! params.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);
|
||||
if (! expolygons_off.empty()) {
|
||||
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
||||
assert(expolygons_off.size() == 1);
|
||||
std::swap(expolygon_off, expolygons_off.front());
|
||||
}
|
||||
}
|
||||
Polylines chained = PolylineCollection::chained_path_from(
|
||||
std::move(polylines),
|
||||
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||
bool first = true;
|
||||
for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) {
|
||||
if (! first) {
|
||||
// Try to connect the lines.
|
||||
Points &pts_end = polylines_out.back().points;
|
||||
const Point &first_point = it_polyline->points.front();
|
||||
const Point &last_point = pts_end.back();
|
||||
// Distance in X, Y.
|
||||
const Vector distance = last_point - first_point;
|
||||
// TODO: we should also check that both points are on a fill_boundary to avoid
|
||||
// connecting paths on the boundaries of internal regions
|
||||
if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) &&
|
||||
expolygon_off.contains(Line(last_point, first_point))) {
|
||||
// Append the polyline.
|
||||
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The lines cannot be connected.
|
||||
polylines_out.emplace_back(std::move(*it_polyline));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
// paths must be rotated back
|
||||
for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_old; it != polylines_out.end(); ++ it) {
|
||||
// No need to translate, the absolute position is irrelevant.
|
||||
// it->translate(- direction.second(0), - direction.second(1));
|
||||
it->rotate(direction.first);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
79
src/libslic3r/Fill/FillRectilinear.hpp
Normal file
79
src/libslic3r/Fill/FillRectilinear.hpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#ifndef slic3r_FillRectilinear_hpp_
|
||||
#define slic3r_FillRectilinear_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FillRectilinear : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillRectilinear(*this); };
|
||||
virtual ~FillRectilinear() {}
|
||||
|
||||
protected:
|
||||
virtual void _fill_surface_single(
|
||||
const FillParams ¶ms,
|
||||
unsigned int thickness_layers,
|
||||
const std::pair<float, Point> &direction,
|
||||
ExPolygon &expolygon,
|
||||
Polylines &polylines_out);
|
||||
|
||||
coord_t _min_spacing;
|
||||
coord_t _line_spacing;
|
||||
// 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.
|
||||
virtual bool _horizontal_lines() const { return false; }
|
||||
|
||||
virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const
|
||||
{ return Line(Point(x, y_min), Point(x, y_max)); }
|
||||
|
||||
virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) {
|
||||
return dist_X <= this->_diagonal_distance
|
||||
&& dist_Y <= this->_diagonal_distance;
|
||||
}
|
||||
};
|
||||
|
||||
class FillLine : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
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 ~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; }
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear_hpp_
|
1475
src/libslic3r/Fill/FillRectilinear2.cpp
Normal file
1475
src/libslic3r/Fill/FillRectilinear2.cpp
Normal file
File diff suppressed because it is too large
Load diff
74
src/libslic3r/Fill/FillRectilinear2.hpp
Normal file
74
src/libslic3r/Fill/FillRectilinear2.hpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef slic3r_FillRectilinear2_hpp_
|
||||
#define slic3r_FillRectilinear2_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.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, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, 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, const FillParams ¶ms);
|
||||
|
||||
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, const FillParams ¶ms);
|
||||
|
||||
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, const FillParams ¶ms);
|
||||
|
||||
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, const FillParams ¶ms);
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear2_hpp_
|
1642
src/libslic3r/Fill/FillRectilinear3.cpp
Normal file
1642
src/libslic3r/Fill/FillRectilinear3.cpp
Normal file
File diff suppressed because it is too large
Load diff
83
src/libslic3r/Fill/FillRectilinear3.hpp
Normal file
83
src/libslic3r/Fill/FillRectilinear3.hpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#ifndef slic3r_FillRectilinear3_hpp_
|
||||
#define slic3r_FillRectilinear3_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FillRectilinear3 : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillRectilinear3(*this); };
|
||||
virtual ~FillRectilinear3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
struct FillDirParams
|
||||
{
|
||||
FillDirParams(coordf_t spacing, double angle, coordf_t pattern_shift = 0.f) :
|
||||
spacing(spacing), angle(angle), pattern_shift(pattern_shift) {}
|
||||
coordf_t spacing;
|
||||
double angle;
|
||||
coordf_t pattern_shift;
|
||||
};
|
||||
|
||||
protected:
|
||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, std::vector<FillDirParams> &fill_dir_params, Polylines &polylines_out);
|
||||
};
|
||||
|
||||
class FillGrid3 : public FillRectilinear3
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillGrid3(*this); };
|
||||
virtual ~FillGrid3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
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 FillTriangles3 : public FillRectilinear3
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillTriangles3(*this); };
|
||||
virtual ~FillTriangles3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
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 FillStars3 : public FillRectilinear3
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillStars3(*this); };
|
||||
virtual ~FillStars3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
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 FillCubic3 : public FillRectilinear3
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillCubic3(*this); };
|
||||
virtual ~FillCubic3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear3_hpp_
|
Loading…
Add table
Add a link
Reference in a new issue