Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port of Cura's multi-material interlocking #5775

Merged
merged 7 commits into from
Jun 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/libslic3r/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,12 @@ set(lisbslic3r_sources
Shape/TextShape.cpp
calib.hpp
calib.cpp
GCode/Thumbnails.cpp
GCode/Thumbnails.hpp
GCode/Thumbnails.cpp
GCode/Thumbnails.hpp
Interlocking/InterlockingGenerator.hpp
Interlocking/InterlockingGenerator.cpp
Interlocking/VoxelUtils.hpp
Interlocking/VoxelUtils.cpp
)

if (APPLE)
Expand Down
3 changes: 3 additions & 0 deletions src/libslic3r/ClipperUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,9 @@ Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons& poly1, const Slic3r::ExPol
Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) {
return _clipper_ex(ClipperLib::ctXor, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset);
}
Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) {
return _clipper_ex(ClipperLib::ctXor, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset);
}

template<typename PathsProvider1, typename PathsProvider2>
Polylines _clipper_pl_open(ClipperLib::ClipType clipType, PathsProvider1 &&subject, PathsProvider2 &&clip)
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/ClipperUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject);
ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject);

Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);

Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject);

Expand Down
330 changes: 330 additions & 0 deletions src/libslic3r/Interlocking/InterlockingGenerator.cpp

Large diffs are not rendered by default.

172 changes: 172 additions & 0 deletions src/libslic3r/Interlocking/InterlockingGenerator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright (c) 2022 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef INTERLOCKING_GENERATOR_HPP
#define INTERLOCKING_GENERATOR_HPP

#include "../Print.hpp"
#include "VoxelUtils.hpp"

namespace Slic3r {

/*!
* Class for generating an interlocking structure between two adjacent models of a different extruder.
*
* The structure consists of horizontal beams of the two materials interlaced.
* In the z direction the direction of these beams is alternated with 90*.
*
* Example with two materials # and O
* Even beams: Odd beams:
* ###### ##OO##OO
* OOOOOO ##OO##OO
* ###### ##OO##OO
* OOOOOO ##OO##OO
*
* One material of a single cell of the structure looks like this:
* .-*-.
* .-* *-.
* |*-. *-.
* | *-. *-.
* .-* *-. *-. *-.
* .-* *-. *-. .-*|
* .-* .-* *-. *-.-* |
* |*-. .-* .-* *-. | .-*
* | *-.-* .-* *-|-*
* *-. | .-*
* *-|-*
*
* We set up a voxel grid of (2*beam_w,2*beam_w,2*beam_h) and mark all the voxels which contain both meshes.
* We then remove all voxels which also contain air, so that the interlocking pattern will not be visible from the outside.
* We then generate and combine the polygons for each voxel and apply those areas to the outlines ofthe meshes.
*/
class InterlockingGenerator
{
public:
/*!
* Generate an interlocking structure between each two adjacent meshes.
*/
static void generate_interlocking_structure(PrintObject* print_object);

private:
/*!
* Generate an interlocking structure between two meshes
*/
void generateInterlockingStructure() const;

/*!
* Private class for storing some variables used in the computation of the interlocking structure between two meshes.
* \param region_a_index The first region
* \param region_b_index The second region
* \param rotation The angle by which to rotate the interlocking pattern
* \param cell_size The size of a voxel cell in (coord_t, coord_t, layer_count)
* \param beam_layer_count The number of layers for the height of the beams
* \param interface_dilation The thicknening kernel for the interface
* \param air_dilation The thickening kernel applied to air so that cells near the outside of the model won't be generated
* \param air_filtering Whether to fully remove all of the interlocking cells which would be visible on the outside (i.e. touching air).
* If no air filtering then those cells will be cut off in the middle of a beam.
*/
InterlockingGenerator(PrintObject& print_object,
const size_t region_a_index,
const size_t region_b_index,
const coord_t beam_width,
const coord_t boundary_avoidance,
const float rotation,
const Vec3crd& cell_size,
const coord_t beam_layer_count,
const DilationKernel& interface_dilation,
const DilationKernel& air_dilation,
const bool air_filtering)
: print_object(print_object)
, region_a_index(region_a_index)
, region_b_index(region_b_index)
, beam_width(beam_width)
, boundary_avoidance(boundary_avoidance)
, vu(cell_size)
, rotation(rotation)
, cell_size(cell_size)
, beam_layer_count(beam_layer_count)
, interface_dilation(interface_dilation)
, air_dilation(air_dilation)
, air_filtering(air_filtering)
{}

/*! Given two polygons, return the parts that border on air, and grow 'perpendicular' up to 'detect' distance.
*
* \param a The first polygon.
* \param b The second polygon.
* \param detec The expand distance. (Not equal to offset, but a series of small offsets and differences).
* \return A pair of polygons that repressent the 'borders' of a and b, but expanded 'perpendicularly'.
*/
std::pair<ExPolygons, ExPolygons> growBorderAreasPerpendicular(const ExPolygons& a, const ExPolygons& b, const coord_t& detect) const;

/*! Special handling for thin strips of material.
*
* Expand the meshes into each other where they need it, namely when a thin strip of material needs to be attached.
* \param has_all_meshes Only do this special handling if there's actually microstructure nearby that needs to be adhered to.
*/
void handleThinAreas(const std::unordered_set<GridPoint3>& has_all_meshes) const;

/*!
* Compute the voxels overlapping with the shell of both models.
* This includes the walls, but also top/bottom skin.
*
* \param kernel The dilation kernel to give the returned voxel shell more thickness
* \return The shell voxels for mesh a and those for mesh b
*/
std::vector<std::unordered_set<GridPoint3>> getShellVoxels(const DilationKernel& kernel) const;

/*!
* Compute the voxels overlapping with the shell of some layers.
* This includes the walls, but also top/bottom skin.
*
* \param layers The layer outlines for which to compute the shell voxels
* \param kernel The dilation kernel to give the returned voxel shell more thickness
* \param[out] cells The output cells which elong to the shell
*/
void addBoundaryCells(const std::vector<ExPolygons>& layers, const DilationKernel& kernel, std::unordered_set<GridPoint3>& cells) const;

/*!
* Compute the regions occupied by both models.
*
* A morphological close is performed so that we don't register small gaps between the two models as being separate.
* \return layer_regions The computed layer regions
*/
std::vector<ExPolygons> computeUnionedVolumeRegions() const;

/*!
* Generate the polygons for the beams of a single cell
* \return cell_area_per_mesh_per_layer The output polygons for each beam
*/
std::vector<std::vector<ExPolygons>> generateMicrostructure() const;

/*!
* Change the outlines of the meshes with the computed interlocking structure.
*
* \param cells The cells where we want to apply the interlocking structure.
* \param layer_regions The total volume of the two meshes combined (and small gaps closed)
*/
void applyMicrostructureToOutlines(const std::unordered_set<GridPoint3>& cells, const std::vector<ExPolygons>& layer_regions) const;

static const coord_t ignored_gap_ = 100u; //!< Distance between models to be considered next to each other so that an interlocking structure will be generated there

PrintObject& print_object;
const size_t region_a_index;
const size_t region_b_index;
const coord_t beam_width;
const coord_t boundary_avoidance;

const VoxelUtils vu;

const float rotation;
const Vec3crd cell_size;
const coord_t beam_layer_count;
const DilationKernel interface_dilation;
const DilationKernel air_dilation;
// Whether to fully remove all of the interlocking cells which would be visible on the outside. If no air filtering then those cells
// will be cut off midway in a beam.
const bool air_filtering;
};

} // namespace Slic3r

#endif // INTERLOCKING_GENERATOR_HPP
Loading
Loading