mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-14 02:07:54 -06:00
Port of Cura's multi-material interlocking (#5775)
* Init port of Cura's MM interlocking * Refactor a bit * Fix crash when bottom surface is multi-color * Fix crash when boundary avoidance is 0 * Add config --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
8ccf0edbc2
commit
4145f45e42
15 changed files with 1029 additions and 5 deletions
218
src/libslic3r/Interlocking/VoxelUtils.cpp
Normal file
218
src/libslic3r/Interlocking/VoxelUtils.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
// Copyright (c) 2022 Ultimaker B.V.
|
||||
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include "VoxelUtils.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
#include "../Fill/FillRectilinear.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
DilationKernel::DilationKernel(GridPoint3 kernel_size, DilationKernel::Type type)
|
||||
: kernel_size_(kernel_size)
|
||||
, type_(type)
|
||||
{
|
||||
coord_t mult = kernel_size.x() * kernel_size.y() * kernel_size.z(); // multiplier for division to avoid rounding and to avoid use of floating point numbers
|
||||
relative_cells_.reserve(mult);
|
||||
GridPoint3 half_kernel = kernel_size / 2;
|
||||
|
||||
GridPoint3 start = -half_kernel;
|
||||
GridPoint3 end = kernel_size - half_kernel;
|
||||
for (coord_t x = start.x(); x < end.x(); x++)
|
||||
{
|
||||
for (coord_t y = start.y(); y < end.y(); y++)
|
||||
{
|
||||
for (coord_t z = start.z(); z < end.z(); z++)
|
||||
{
|
||||
GridPoint3 current(x, y, z);
|
||||
if (type != Type::CUBE)
|
||||
{
|
||||
GridPoint3 limit((x < 0) ? start.x() : end.x() - 1, (y < 0) ? start.y() : end.y() - 1, (z < 0) ? start.z() : end.z() - 1);
|
||||
if (limit.x() == 0)
|
||||
limit.x() = 1;
|
||||
if (limit.y() == 0)
|
||||
limit.y() = 1;
|
||||
if (limit.z() == 0)
|
||||
limit.z() = 1;
|
||||
const GridPoint3 rel_dists = (mult * current).array() / limit.array();
|
||||
if ((type == Type::DIAMOND && rel_dists.x() + rel_dists.y() + rel_dists.z() > mult) || (type == Type::PRISM && rel_dists.x() + rel_dists.y() > mult))
|
||||
{
|
||||
continue; // don't consider this cell
|
||||
}
|
||||
}
|
||||
relative_cells_.emplace_back(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool VoxelUtils::walkLine(Vec3crd start, Vec3crd end, const std::function<bool(GridPoint3)>& process_cell_func) const
|
||||
{
|
||||
Vec3crd diff = end - start;
|
||||
|
||||
const GridPoint3 start_cell = toGridPoint(start);
|
||||
const GridPoint3 end_cell = toGridPoint(end);
|
||||
if (start_cell == end_cell)
|
||||
{
|
||||
return process_cell_func(start_cell);
|
||||
}
|
||||
|
||||
Vec3crd current_cell = start_cell;
|
||||
while (true)
|
||||
{
|
||||
bool continue_ = process_cell_func(current_cell);
|
||||
|
||||
if (! continue_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int stepping_dim = -1; // dimension in which the line next exits the current cell
|
||||
double percentage_along_line = std::numeric_limits<double>::max();
|
||||
for (int dim = 0; dim < 3; dim++)
|
||||
{
|
||||
if (diff[dim] == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
coord_t crossing_boundary = toLowerCoord(current_cell[dim], dim) + (diff[dim] > 0) * cell_size_[dim];
|
||||
double percentage_along_line_here = (crossing_boundary - start[dim]) / static_cast<double>(diff[dim]);
|
||||
if (percentage_along_line_here < percentage_along_line)
|
||||
{
|
||||
percentage_along_line = percentage_along_line_here;
|
||||
stepping_dim = dim;
|
||||
}
|
||||
}
|
||||
assert(stepping_dim != -1);
|
||||
if (percentage_along_line > 1.0)
|
||||
{
|
||||
// next cell is beyond the end
|
||||
return true;
|
||||
}
|
||||
current_cell[stepping_dim] += (diff[stepping_dim] > 0) ? 1 : -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool VoxelUtils::walkPolygons(const ExPolygon& polys, coord_t z, const std::function<bool(GridPoint3)>& process_cell_func) const
|
||||
{
|
||||
for (const Polygon& poly : to_polygons(polys))
|
||||
{
|
||||
Point last = poly.back();
|
||||
for (Point p : poly)
|
||||
{
|
||||
bool continue_ = walkLine(Vec3crd(last.x(), last.y(), z), Vec3crd(p.x(), p.y(), z), process_cell_func);
|
||||
if (! continue_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
last = p;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VoxelUtils::walkDilatedPolygons(const ExPolygon& polys, coord_t z, const DilationKernel& kernel, const std::function<bool(GridPoint3)>& process_cell_func) const
|
||||
{
|
||||
ExPolygon translated = polys;
|
||||
GridPoint3 k = kernel.kernel_size_;
|
||||
k.x() %= 2;
|
||||
k.y() %= 2;
|
||||
k.z() %= 2;
|
||||
const Vec3crd translation = (Vec3crd(1, 1, 1) - k).array() * cell_size_.array() / 2;
|
||||
if (translation.x() && translation.y())
|
||||
{
|
||||
translated.translate(Point(translation.x(), translation.y()));
|
||||
}
|
||||
return walkPolygons(translated, z + translation.z(), dilate(kernel, process_cell_func));
|
||||
}
|
||||
|
||||
bool VoxelUtils::walkAreas(const ExPolygon& polys, coord_t z, const std::function<bool(GridPoint3)>& process_cell_func) const
|
||||
{
|
||||
ExPolygon translated = polys;
|
||||
const Vec3crd translation = -cell_size_ / 2; // offset half a cell so that the dots of spreadDotsArea are centered on the middle of the cell isntead of the lower corners.
|
||||
if (translation.x() && translation.y())
|
||||
{
|
||||
translated.translate(Point(translation.x(), translation.y()));
|
||||
}
|
||||
return _walkAreas(translated, z, process_cell_func);
|
||||
}
|
||||
|
||||
static Points spreadDotsArea(const ExPolygon& polygons, Point grid_size)
|
||||
{
|
||||
std::unique_ptr<Fill> filler(Fill::new_from_type(ipAlignedRectilinear));
|
||||
filler->angle = Geometry::deg2rad(90.f);
|
||||
filler->spacing = unscaled(grid_size.x());
|
||||
filler->bounding_box = get_extents(polygons);
|
||||
|
||||
FillParams params;
|
||||
params.density = 1.f;
|
||||
params.anchor_length_max = 0;
|
||||
|
||||
Surface surface(stInternal, polygons);
|
||||
auto polylines = filler->fill_surface(&surface, params);
|
||||
|
||||
Points result;
|
||||
for (const Polyline& line : polylines) {
|
||||
assert(line.size() == 2);
|
||||
Point a = line[0];
|
||||
Point b = line[1];
|
||||
assert(a.x() == b.x());
|
||||
if (a.y() > b.y()) {
|
||||
std::swap(a, b);
|
||||
}
|
||||
for (coord_t y = a.y() - (a.y() % grid_size.y()) - grid_size.y(); y < b.y(); y += grid_size.y()) {
|
||||
if (y < a.y())
|
||||
continue;
|
||||
result.emplace_back(a.x(), y);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VoxelUtils::_walkAreas(const ExPolygon& polys, coord_t z, const std::function<bool(GridPoint3)>& process_cell_func) const
|
||||
{
|
||||
Points skin_points = spreadDotsArea(polys, Point(cell_size_.x(), cell_size_.y()));
|
||||
for (Point p : skin_points)
|
||||
{
|
||||
bool continue_ = process_cell_func(toGridPoint(Vec3crd(p.x() + cell_size_.x() / 2, p.y() + cell_size_.y() / 2, z)));
|
||||
if (! continue_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VoxelUtils::walkDilatedAreas(const ExPolygon& polys, coord_t z, const DilationKernel& kernel, const std::function<bool(GridPoint3)>& process_cell_func) const
|
||||
{
|
||||
ExPolygon translated = polys;
|
||||
GridPoint3 k = kernel.kernel_size_;
|
||||
k.x() %= 2;
|
||||
k.y() %= 2;
|
||||
k.z() %= 2;
|
||||
const Vec3crd translation = (Vec3crd(1, 1, 1) - k).array() * cell_size_.array() / 2 // offset half a cell when using an even kernel
|
||||
- cell_size_.array() / 2; // offset half a cell so that the dots of spreadDotsArea are centered on the middle of the cell isntead of the lower corners.
|
||||
if (translation.x() && translation.y())
|
||||
{
|
||||
translated.translate(Point(translation.x(), translation.y()));
|
||||
}
|
||||
return _walkAreas(translated, z + translation.z(), dilate(kernel, process_cell_func));
|
||||
}
|
||||
|
||||
std::function<bool(GridPoint3)> VoxelUtils::dilate(const DilationKernel& kernel, const std::function<bool(GridPoint3)>& process_cell_func) const
|
||||
{
|
||||
return [&process_cell_func, &kernel](GridPoint3 loc)
|
||||
{
|
||||
for (const GridPoint3& rel : kernel.relative_cells_)
|
||||
{
|
||||
bool continue_ = process_cell_func(loc + rel);
|
||||
if (! continue_)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
} // namespace cura
|
Loading…
Add table
Add a link
Reference in a new issue