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

Add voronoi utility functions #1984

Closed
wants to merge 4 commits into from
Closed
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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ target_compile_definitions(CuraEngine PRIVATE VERSION=\"${CURA_ENGINE_VERSION}\"

# Compiling the test environment.
if (ENABLE_TESTING OR ENABLE_BENCHMARKS)
set(TESTS_HELPERS_SRC tests/ReadTestPolygons.cpp)
set(TESTS_HELPERS_SRC tests/ReadTestPolygons.cpp tests/ReadTestSettings.cpp)

set(TESTS_SRC_ARCUS)
if (ENABLE_ARCUS)
Expand Down
69 changes: 52 additions & 17 deletions include/utils/SVG.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
//Copyright (c) 2022 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.
// Copyright (c) 2022 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.

#ifndef SVG_H
#define SVG_H

#include <concepts>
#include <stdio.h> // for file output

#include <boost/polygon/voronoi.hpp>

#include "AABB.h"
#include "ExtrusionLine.h" //To accept variable-width paths.
#include "IntPoint.h"
Expand All @@ -19,7 +22,8 @@ class FPoint3;
class SVG : NoCopy
{
public:
enum class Color {
enum class Color
{
BLACK,
WHITE,
GRAY,
Expand All @@ -40,18 +44,20 @@ class SVG : NoCopy
Color color;
int r, g, b;
ColorObject(Color color)
: is_enum(true)
, color(color)
{}
: is_enum(true)
, color(color)
{
}
ColorObject(int r, int g, int b)
: is_enum(false)
, r(r)
, g(g)
, b(b)
{}
: is_enum(false)
, r(r)
, g(g)
, b(b)
{
}
};
private:

private:
std::string toString(const Color color) const;
std::string toString(const ColorObject& color) const;

Expand Down Expand Up @@ -103,10 +109,10 @@ class SVG : NoCopy

/*!
* \brief Draws a polyline on the canvas.
*
*
* The polyline is the set of line segments between each pair of consecutive
* points in the specified vector.
*
*
* \param polyline A set of points between which line segments must be
* drawn.
* \param color The colour of the line segments. If this is not specified,
Expand All @@ -122,14 +128,14 @@ class SVG : NoCopy

/*!
* \brief Draws a dashed line on the canvas from point A to point B.
*
*
* This is useful in the case where multiple lines may overlap each other.
*
*
* \param a The starting endpoint of the line.
* \param b The ending endpoint of the line.
* \param color The stroke colour of the line.
*/
void writeDashedLine(const Point& a,const Point& b, ColorObject color = Color::BLACK) const;
void writeDashedLine(const Point& a, const Point& b, ColorObject color = Color::BLACK) const;

template<typename... Args>
void printf(const char* txt, Args&&... args) const;
Expand Down Expand Up @@ -188,6 +194,35 @@ class SVG : NoCopy
*/
void writeCoordinateGrid(const coord_t grid_size = MM2INT(1), const Color color = Color::BLACK, const float stroke_width = 0.1, const float font_size = 10) const;

/*!
* Draws the provided Voronoi diagram.
*
* @tparam T numeric type
* @param voronoi The Voronoi diagram to draw.
* @param color The colour to draw the diagram with.
* @param stroke_width The width of the lines.
*/
template<std::floating_point T>
void writeVoronoiDiagram(const boost::polygon::voronoi_diagram<T>& voronoi_diagram, const Color color = Color::BLACK, const float stroke_width = 0.1) const
{
for (const auto& edge : voronoi_diagram.edges())
{
if (! edge.is_finite())
{
continue;
}

const auto& v0 = edge.vertex0();
const auto& v1 = edge.vertex1();

if (v0 == nullptr || v1 == nullptr)
{
continue;
}

writeLine(Point(v0->x(), v0->y()), Point(v1->x(), v1->y()), color, stroke_width);
}
}
};

template<typename... Args>
Expand Down
23 changes: 19 additions & 4 deletions include/utils/polygon.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
#ifndef UTILS_POLYGON_H
#define UTILS_POLYGON_H

#include "../settings/types/Angle.h" //For angles between vertices.
#include "../settings/types/Ratio.h"
#include "IntPoint.h"

#include <algorithm>
#include <algorithm> // std::reverse, fill_n array
#include <assert.h>
Expand All @@ -20,6 +16,10 @@
#include <unordered_map>
#include <vector>

#include "../settings/types/Angle.h" //For angles between vertices.
#include "../settings/types/Ratio.h"
#include "IntPoint.h"

#define CHECK_POLY_ACCESS
#ifdef CHECK_POLY_ACCESS
#define POLY_ASSERT(e) assert(e)
Expand Down Expand Up @@ -1509,6 +1509,21 @@ class Polygons
}

Polygons offset(const std::vector<coord_t>& offset_dists) const;

/*!
* @brief Export the polygon to a WKT string
*
* @param stream The stream to write to
*/
void writeWkt(std::ostream& stream) const;

/*!
* @brief Import the polygon from a WKT string
*
* @param wkt The WKT string to read from
* @return Polygons The polygons read from the stream
*/
static Polygons fromWkt(const std::string& wkt);
};

/*!
Expand Down
91 changes: 78 additions & 13 deletions src/WallsComputation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
// CuraEngine is released under the terms of the AGPLv3 or higher

#include "WallsComputation.h"

#include <fstream>
#include <iostream>

#include <fmt/format.h>
#include <range/v3/to_container.hpp>
#include <range/v3/view/c_str.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/transform.hpp>

#include "Application.h"
#include "ExtruderTrain.h"
#include "Slice.h"
Expand All @@ -13,7 +23,9 @@
namespace cura
{

WallsComputation::WallsComputation(const Settings& settings, const LayerIndex layer_nr) : settings(settings), layer_nr(layer_nr)
WallsComputation::WallsComputation(const Settings& settings, const LayerIndex layer_nr)
: settings(settings)
, layer_nr(layer_nr)
{
}

Expand All @@ -35,7 +47,8 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t

const bool spiralize = settings.get<bool>("magic_spiralize");
const size_t alternate = ((layer_nr % 2) + 2) % 2;
if (spiralize && layer_nr < LayerIndex(settings.get<size_t>("initial_bottom_layers")) && alternate == 1) //Add extra insets every 2 layers when spiralizing. This makes bottoms of cups watertight.
if (spiralize && layer_nr < LayerIndex(settings.get<size_t>("initial_bottom_layers"))
&& alternate == 1) // Add extra insets every 2 layers when spiralizing. This makes bottoms of cups watertight.
{
wall_count += 5;
}
Expand All @@ -55,8 +68,7 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t
// When spiralizing, generate the spiral insets using simple offsets instead of generating toolpaths
if (spiralize)
{
const bool recompute_outline_based_on_outer_wall =
settings.get<bool>("support_enable") && !settings.get<bool>("fill_outline_gaps");
const bool recompute_outline_based_on_outer_wall = settings.get<bool>("support_enable") && ! settings.get<bool>("fill_outline_gaps");

generateSpiralInsets(part, line_width_0, wall_0_inset, recompute_outline_based_on_outer_wall);
if (layer_nr <= static_cast<LayerIndex>(settings.get<size_t>("initial_bottom_layers")))
Expand All @@ -72,7 +84,7 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t
part->wall_toolpaths = wall_tool_paths.getToolPaths();
part->inner_area = wall_tool_paths.getInnerContour();
}
part->outline = PolygonsPart { Simplify(settings).polygon(part->outline) };
part->outline = PolygonsPart{ Simplify(settings).polygon(part->outline) };
part->print_outline = part->outline;
}

Expand All @@ -84,16 +96,69 @@ void WallsComputation::generateWalls(SliceLayerPart* part, SectionType section_t
*/
void WallsComputation::generateWalls(SliceLayer* layer, SectionType section)
{
for(SliceLayerPart& part : layer->parts)
// TODO remove this block before merging PR!
// This code exists to generate the test wkt, and setting files
{
std::ofstream SettingsFile("settings.txt");
SettingsFile.clear();
for (auto [key, value] : settings.getFlattendSettings())
{
if (value == "")
{
continue;
}
SettingsFile << key << "=" << value << std::endl;
}
SettingsFile.close();

std::ofstream PolygonFile("slice_polygon.wkt");
PolygonFile.clear();

std::vector<Polygons> multi_polygons;
for (const auto& part : layer->parts)
{
multi_polygons.push_back(part.outline);
}

PolygonFile << "MULTIPOLYGON (";
const auto paths_str = multi_polygons
| ranges::views::transform(
[](const auto& path)
{
const auto path_str = path
| ranges::views::transform(
[](const auto& path)
{
const auto path_str = path
| ranges::views::transform(
[](const auto& point)
{
return fmt::format("{} {}", point.X, point.Y);
})
| ranges::views::join(ranges::views::c_str(", ")) | ranges::to<std::string>();
return "(" + path_str + ")";
})
| ranges::views::join(ranges::views::c_str(" ")) | ranges::to<std::string>();
return "(" + path_str + ")";
})
| ranges::views::join(ranges::views::c_str(", ")) | ranges::to<std::string>();

PolygonFile << paths_str;
PolygonFile << ")";

PolygonFile.close();
}

for (SliceLayerPart& part : layer->parts)
{
generateWalls(&part, section);
}

//Remove the parts which did not generate a wall. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 wall line.
if(settings.get<size_t>("wall_line_count") >= 1 && !settings.get<bool>("fill_outline_gaps"))
// Remove the parts which did not generate a wall. As these parts are too small to print,
// and later code can now assume that there is always minimal 1 wall line.
if (settings.get<size_t>("wall_line_count") >= 1 && ! settings.get<bool>("fill_outline_gaps"))
{
for(size_t part_idx = 0; part_idx < layer->parts.size(); part_idx++)
for (size_t part_idx = 0; part_idx < layer->parts.size(); part_idx++)
{
if (layer->parts[part_idx].wall_toolpaths.empty() && layer->parts[part_idx].spiral_wall.empty())
{
Expand All @@ -108,11 +173,11 @@ void WallsComputation::generateWalls(SliceLayer* layer, SectionType section)
}
}

void WallsComputation::generateSpiralInsets(SliceLayerPart *part, coord_t line_width_0, coord_t wall_0_inset, bool recompute_outline_based_on_outer_wall)
void WallsComputation::generateSpiralInsets(SliceLayerPart* part, coord_t line_width_0, coord_t wall_0_inset, bool recompute_outline_based_on_outer_wall)
{
part->spiral_wall = part->outline.offset(-line_width_0 / 2 - wall_0_inset);

//Optimize the wall. This prevents buffer underruns in the printer firmware, and reduces processing time in CuraEngine.
// Optimize the wall. This prevents buffer underruns in the printer firmware, and reduces processing time in CuraEngine.
const ExtruderTrain& train_wall = settings.get<ExtruderTrain&>("wall_0_extruder_nr");
part->spiral_wall = Simplify(train_wall.settings).polygon(part->spiral_wall);
part->spiral_wall.removeDegenerateVerts();
Expand All @@ -126,4 +191,4 @@ void WallsComputation::generateSpiralInsets(SliceLayerPart *part, coord_t line_w
}
}

}//namespace cura
} // namespace cura
Loading
Loading