ENH: add Clipper2::Union and fix a command line hang issue
jira: STUDIO-9623 Change-Id: I8b40f66fed0fc9e5b13f0f10337267065fef1056
This commit is contained in:
parent
4f1ad8016e
commit
b5e3b96764
|
@ -295,7 +295,8 @@ namespace Clipper2Lib {
|
||||||
typedef typename std::vector<PolyPath64*>::const_iterator pp64_itor;
|
typedef typename std::vector<PolyPath64*>::const_iterator pp64_itor;
|
||||||
public:
|
public:
|
||||||
PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
|
PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {}
|
||||||
PolyPath64* operator [] (size_t index) { return static_cast<PolyPath64*>(childs_[index]); }
|
PolyPath64 *operator[](size_t index) { return static_cast<PolyPath64 *>(childs_[index]); }
|
||||||
|
PolyPath64 *Childs(size_t index) const { return static_cast<PolyPath64 *>(childs_[index]); }
|
||||||
pp64_itor begin() const { return childs_.cbegin(); }
|
pp64_itor begin() const { return childs_.cbegin(); }
|
||||||
pp64_itor end() const { return childs_.cend(); }
|
pp64_itor end() const { return childs_.cend(); }
|
||||||
|
|
||||||
|
|
|
@ -781,7 +781,7 @@ const Polygons& WallToolPaths::getInnerContour()
|
||||||
}
|
}
|
||||||
return inner_contour;
|
return inner_contour;
|
||||||
}
|
}
|
||||||
|
Polygons EmptyPolygons;
|
||||||
const Polygons& WallToolPaths::getFirstWallContour()
|
const Polygons& WallToolPaths::getFirstWallContour()
|
||||||
{
|
{
|
||||||
if (!toolpaths_generated && inset_count > 0)
|
if (!toolpaths_generated && inset_count > 0)
|
||||||
|
@ -790,7 +790,7 @@ const Polygons& WallToolPaths::getFirstWallContour()
|
||||||
}
|
}
|
||||||
else if(inset_count == 0)
|
else if(inset_count == 0)
|
||||||
{
|
{
|
||||||
return {};
|
return EmptyPolygons;
|
||||||
}
|
}
|
||||||
return first_wall_contour;
|
return first_wall_contour;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "Clipper2Utils.hpp"
|
#include "Clipper2Utils.hpp"
|
||||||
|
#include "libslic3r.h"
|
||||||
|
#include "clipper2/clipper.h"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -33,6 +35,66 @@ Clipper2Lib::Paths64 Slic3rPoints_to_Paths64(const std::vector<T>& in)
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Points Path64ToPoints(const Clipper2Lib::Path64& path64)
|
||||||
|
{
|
||||||
|
Points points;
|
||||||
|
points.reserve(path64.size());
|
||||||
|
for (const Clipper2Lib::Point64 &point64 : path64) points.emplace_back(std::move(Slic3r::Point(point64.x, point64.y)));
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ExPolygons PolyTreeToExPolygons(Clipper2Lib::PolyTree64 &&polytree)
|
||||||
|
{
|
||||||
|
struct Inner
|
||||||
|
{
|
||||||
|
static void PolyTreeToExPolygonsRecursive(Clipper2Lib::PolyTree64 &&polynode, ExPolygons *expolygons)
|
||||||
|
{
|
||||||
|
size_t cnt = expolygons->size();
|
||||||
|
expolygons->resize(cnt + 1);
|
||||||
|
(*expolygons)[cnt].contour.points = Path64ToPoints(polynode.Polygon());
|
||||||
|
|
||||||
|
(*expolygons)[cnt].holes.resize(polynode.Count());
|
||||||
|
for (int i = 0; i < polynode.Count(); ++i) {
|
||||||
|
(*expolygons)[cnt].holes[i].points = Path64ToPoints(polynode[i]->Polygon());
|
||||||
|
// Add outer polygons contained by (nested within) holes.
|
||||||
|
for (int j = 0; j < polynode[i]->Count(); ++j) PolyTreeToExPolygonsRecursive(std::move(*polynode[i]->Childs(j)), expolygons);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t PolyTreeCountExPolygons(const Clipper2Lib::PolyPath64& polynode)
|
||||||
|
{
|
||||||
|
size_t cnt = 1;
|
||||||
|
for (size_t i = 0; i < polynode.Count(); ++i) {
|
||||||
|
for (size_t j = 0; j < polynode.Childs(i)->Count(); ++j) cnt += PolyTreeCountExPolygons(*polynode.Childs(i)->Childs(j));
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ExPolygons retval;
|
||||||
|
size_t cnt = 0;
|
||||||
|
for (int i = 0; i < polytree.Count(); ++i) cnt += Inner::PolyTreeCountExPolygons(*polytree[i]);
|
||||||
|
retval.reserve(cnt);
|
||||||
|
for (int i = 0; i < polytree.Count(); ++i) Inner::PolyTreeToExPolygonsRecursive(std::move(*polytree[i]), &retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
Clipper2Lib::Paths64 Slic3rExPolygons_to_Paths64(const ExPolygons& in)
|
||||||
|
{
|
||||||
|
Clipper2Lib::Paths64 out;
|
||||||
|
out.reserve(in.size());
|
||||||
|
for (const ExPolygon& expolygon : in) {
|
||||||
|
for (size_t i = 0; i < expolygon.num_contours(); i++) {
|
||||||
|
const auto &poly = expolygon.contour_or_hole(i);
|
||||||
|
Clipper2Lib::Path64 path;
|
||||||
|
path.reserve(poly.points.size());
|
||||||
|
for (const Slic3r::Point &point : poly.points) path.emplace_back(std::move(Clipper2Lib::Point64(point.x(), point.y())));
|
||||||
|
out.emplace_back(std::move(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
Polylines _clipper2_pl_open(Clipper2Lib::ClipType clipType, const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
|
Polylines _clipper2_pl_open(Clipper2Lib::ClipType clipType, const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
|
||||||
{
|
{
|
||||||
Clipper2Lib::Clipper64 c;
|
Clipper2Lib::Clipper64 c;
|
||||||
|
@ -57,4 +119,19 @@ Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic
|
||||||
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
|
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
|
||||||
{ return _clipper2_pl_open(Clipper2Lib::ClipType::Difference, subject, clip); }
|
{ return _clipper2_pl_open(Clipper2Lib::ClipType::Difference, subject, clip); }
|
||||||
|
|
||||||
|
ExPolygons union_ex2(const ExPolygons& expolygons)
|
||||||
|
{
|
||||||
|
Clipper2Lib::Clipper64 c;
|
||||||
|
c.AddSubject(Slic3rExPolygons_to_Paths64(expolygons));
|
||||||
|
|
||||||
|
Clipper2Lib::ClipType ct = Clipper2Lib::ClipType::Union;
|
||||||
|
Clipper2Lib::FillRule fr = Clipper2Lib::FillRule::NonZero;
|
||||||
|
Clipper2Lib::PolyTree64 solution;
|
||||||
|
c.Execute(ct, fr, solution);
|
||||||
|
|
||||||
|
ExPolygons results = PolyTreeToExPolygons(std::move(solution));
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
#ifndef slic3r_Clipper2Utils_hpp_
|
#ifndef slic3r_Clipper2Utils_hpp_
|
||||||
#define slic3r_Clipper2Utils_hpp_
|
#define slic3r_Clipper2Utils_hpp_
|
||||||
|
|
||||||
#include "libslic3r.h"
|
|
||||||
#include "clipper2/clipper.h"
|
|
||||||
#include "Polygon.hpp"
|
#include "Polygon.hpp"
|
||||||
#include "Polyline.hpp"
|
#include "Polyline.hpp"
|
||||||
|
|
||||||
|
@ -10,7 +8,7 @@ namespace Slic3r {
|
||||||
|
|
||||||
Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
|
Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
|
||||||
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
|
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
|
||||||
|
ExPolygons union_ex2(const ExPolygons &expolygons);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#include "SVG.hpp"
|
#include "SVG.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
// #include "pugixml/pugixml.hpp"
|
||||||
#include <boost/nowide/cstdio.hpp>
|
#include <boost/nowide/cstdio.hpp>
|
||||||
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -331,6 +332,132 @@ void SVG::add_comment(const std::string comment)
|
||||||
fprintf(this->f, "<!-- %s -->\n", comment.c_str());
|
fprintf(this->f, "<!-- %s -->\n", comment.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to parse the SVG path data
|
||||||
|
Points ParseSVGPath(const std::string &pathData)
|
||||||
|
{
|
||||||
|
Points points;
|
||||||
|
Vec2d currentPoint = {0, 0};
|
||||||
|
char command = 0;
|
||||||
|
std::istringstream stream(pathData);
|
||||||
|
|
||||||
|
while (stream) {
|
||||||
|
// Read the command or continue with the previous command
|
||||||
|
if (!std::isdigit(stream.peek()) && stream.peek() != '-' && stream.peek() != '.') { stream >> command; }
|
||||||
|
|
||||||
|
if (command == 'M' || command == 'm') { // Move to
|
||||||
|
double x, y;
|
||||||
|
stream >> x;
|
||||||
|
stream.ignore(1, ','); // Skip the comma, if present
|
||||||
|
stream >> y;
|
||||||
|
|
||||||
|
if (command == 'm') { // Relative
|
||||||
|
currentPoint.x() += x;
|
||||||
|
currentPoint.y() += y;
|
||||||
|
} else { // Absolute
|
||||||
|
currentPoint.x() = x;
|
||||||
|
currentPoint.y() = y;
|
||||||
|
}
|
||||||
|
points.push_back(scaled<coord_t>(currentPoint));
|
||||||
|
} else if (command == 'L' || command == 'l') { // Line to
|
||||||
|
double x, y;
|
||||||
|
stream >> x;
|
||||||
|
stream.ignore(1, ','); // Skip the comma, if present
|
||||||
|
stream >> y;
|
||||||
|
|
||||||
|
if (command == 'l') { // Relative
|
||||||
|
currentPoint.x() += x;
|
||||||
|
currentPoint.y() += y;
|
||||||
|
} else { // Absolute
|
||||||
|
currentPoint.x() = x;
|
||||||
|
currentPoint.y() = y;
|
||||||
|
}
|
||||||
|
points.push_back(scaled<coord_t>(currentPoint));
|
||||||
|
} else if (command == 'Z' || command == 'z') { // Close path
|
||||||
|
if (!points.empty()) {
|
||||||
|
points.push_back(points.front()); // Close the polygon by returning to the start
|
||||||
|
}
|
||||||
|
} else if (command == 'H' || command == 'h') { // Horizontal line
|
||||||
|
double x;
|
||||||
|
stream >> x;
|
||||||
|
|
||||||
|
if (command == 'h') { // Relative
|
||||||
|
currentPoint.x() += x;
|
||||||
|
} else { // Absolute
|
||||||
|
currentPoint.x() = x;
|
||||||
|
}
|
||||||
|
points.push_back(scaled<coord_t>(currentPoint));
|
||||||
|
} else if (command == 'V' || command == 'v') { // Vertical line
|
||||||
|
double y;
|
||||||
|
stream >> y;
|
||||||
|
|
||||||
|
if (command == 'v') { // Relative
|
||||||
|
currentPoint.y() += y;
|
||||||
|
} else { // Absolute
|
||||||
|
currentPoint.y() = y;
|
||||||
|
}
|
||||||
|
points.push_back(scaled<coord_t>(currentPoint));
|
||||||
|
} else if (command == 'z') {
|
||||||
|
if (!points.empty()) {
|
||||||
|
points.push_back(points.front()); // Close path
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stream.ignore(1); // Skip invalid commands or extra spaces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert SVG path to ExPolygon
|
||||||
|
ExPolygon ConvertToExPolygon(const std::vector<std::string> &svgPaths)
|
||||||
|
{
|
||||||
|
ExPolygon exPolygon;
|
||||||
|
|
||||||
|
for (const auto &pathData : svgPaths) {
|
||||||
|
auto points = ParseSVGPath(pathData);
|
||||||
|
if (exPolygon.contour.empty()) {
|
||||||
|
exPolygon.contour.points = points; // First path is outer
|
||||||
|
} else {
|
||||||
|
exPolygon.holes.emplace_back(points); // Subsequent paths are holes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return exPolygon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to load SVG and convert paths to ExPolygons
|
||||||
|
std::vector<ExPolygon> SVG::load(const std::string &svgFilePath)
|
||||||
|
{
|
||||||
|
std::vector<ExPolygon> polygons;
|
||||||
|
/* pugi::xml_document doc;
|
||||||
|
pugi::xml_parse_result result = doc.load_file(svgFilePath.c_str());
|
||||||
|
if (!result) {
|
||||||
|
std::cerr << "Failed to load SVG file: " << result.description() << "\n";
|
||||||
|
return polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Find the root <svg> element
|
||||||
|
pugi::xml_node svgNode = doc.child("svg");
|
||||||
|
if (!svgNode) {
|
||||||
|
std::cerr << "No <svg> element found in file.\n";
|
||||||
|
return polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over <path> elements
|
||||||
|
for (pugi::xml_node pathNode : svgNode.children("path")) {
|
||||||
|
const char *pathData = pathNode.attribute("d").value();
|
||||||
|
if (pathData) {
|
||||||
|
std::vector<std::string> paths = {std::string(pathData)}; // For simplicity, assuming one path per element. You could extract more complex paths if necessary.
|
||||||
|
ExPolygon exPolygon = ConvertToExPolygon(paths);
|
||||||
|
polygons.push_back(exPolygon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return polygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SVG::Close()
|
void SVG::Close()
|
||||||
{
|
{
|
||||||
fprintf(this->f, "</svg>\n");
|
fprintf(this->f, "</svg>\n");
|
||||||
|
@ -411,4 +538,234 @@ void SVG::export_expolygons(const char *path, const std::vector<std::pair<Slic3r
|
||||||
svg.Close();
|
svg.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// JSON serialization for Point using compact format [x, y]
|
||||||
|
void to_json(nlohmann::json &j, const Point &p) { j = nlohmann::json{p.x(), p.y()}; }
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, Point &p)
|
||||||
|
{
|
||||||
|
if (j.is_array() && j.size() == 2) {
|
||||||
|
p.x() = j[0].get<coord_t>();
|
||||||
|
p.y() = j[1].get<coord_t>();
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Invalid Point JSON format. Expected [x, y].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialization for Polygon
|
||||||
|
void to_json(nlohmann::json &j, const Polygon &polygon)
|
||||||
|
{
|
||||||
|
j = nlohmann::json::array();
|
||||||
|
for (const auto &point : polygon.points) {
|
||||||
|
j.push_back(point); // Push each point (serialized as [x, y])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, Polygon &polygon)
|
||||||
|
{
|
||||||
|
if (j.is_array()) {
|
||||||
|
polygon.clear();
|
||||||
|
for (const auto &item : j) { polygon.append(item.get<Point>()); }
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Invalid Polygon JSON format. Expected array of points.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Serialization for ExPolygon
|
||||||
|
void to_json(nlohmann::json &j, const ExPolygon &exPolygon) {
|
||||||
|
j = nlohmann::json{{"contour", exPolygon.contour}, {"holes", exPolygon.holes}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json &j, ExPolygon &exPolygon)
|
||||||
|
{
|
||||||
|
if (j.contains("contour")) {
|
||||||
|
j.at("contour").get_to(exPolygon.contour);
|
||||||
|
if (j.contains("holes")) {
|
||||||
|
j.at("holes").get_to(exPolygon.holes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Invalid ExPolygon JSON format. Missing 'contour' or 'holes'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialization for ExPolygons
|
||||||
|
void to_json(nlohmann::json &j, const std::vector<ExPolygon> &exPolygons)
|
||||||
|
{
|
||||||
|
j = nlohmann::json::array();
|
||||||
|
for (const auto &exPolygon : exPolygons) {
|
||||||
|
j.push_back(exPolygon); // Serialize each ExPolygon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json& j, std::vector<ExPolygon>& exPolygons)
|
||||||
|
{
|
||||||
|
if (j.is_array()) {
|
||||||
|
exPolygons.clear();
|
||||||
|
for (const auto& item : j) {
|
||||||
|
exPolygons.push_back(item.get<ExPolygon>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Invalid ExPolygons JSON format. Expected array of ExPolygons.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to dump ExPolygons to JSON
|
||||||
|
void dumpExPolygonToJson(const ExPolygon &exPolygon, const std::string &filePath)
|
||||||
|
{
|
||||||
|
nlohmann::json j = exPolygon;
|
||||||
|
|
||||||
|
// Write JSON to a file
|
||||||
|
std::ofstream file(filePath);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Cannot open file for writing: " << filePath << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file << j.dump(4); // Pretty print with 4 spaces of indentation
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
std::cout << "ExPolygons dumped to " << filePath << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to dump ExPolygons to JSON
|
||||||
|
void dumpExPolygonsToJson(const std::vector<ExPolygon> &exPolygons, const std::string &filePath)
|
||||||
|
{
|
||||||
|
nlohmann::json j = exPolygons;
|
||||||
|
|
||||||
|
// Write JSON to a file
|
||||||
|
std::ofstream file(filePath);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Cannot open file for writing: " << filePath << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file << j.dump(4); // Pretty print with 4 spaces of indentation
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
std::cout << "ExPolygons dumped to " << filePath << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to load ExPolygons from JSON
|
||||||
|
std::vector<ExPolygon> loadExPolygonsFromJson(const std::string &filePath)
|
||||||
|
{
|
||||||
|
std::vector<ExPolygon> exPolygons;
|
||||||
|
|
||||||
|
std::ifstream file(filePath);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Cannot open file for reading: " << filePath << "\n";
|
||||||
|
return exPolygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << file.rdbuf();
|
||||||
|
std::string content = buffer.str(); // Read entire file into string
|
||||||
|
|
||||||
|
nlohmann::json j;
|
||||||
|
try {
|
||||||
|
j = nlohmann::json::parse(content);
|
||||||
|
//file >> j; // Parse JSON from file
|
||||||
|
} catch (const nlohmann::json::parse_error &e) {
|
||||||
|
std::cerr << "JSON parsing error: " << e.what() << std::endl;
|
||||||
|
return exPolygons; // Return empty vector on failure
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "Error: " << e.what() << "\n";
|
||||||
|
file.close();
|
||||||
|
return exPolygons;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Deserialize JSON to std::vector<ExPolygon>
|
||||||
|
//exPolygons = j.get<std::vector<ExPolygon>>();
|
||||||
|
if (j.is_array()) {
|
||||||
|
for (const auto& item : j) {
|
||||||
|
exPolygons.push_back(item.get<ExPolygon>());
|
||||||
|
}
|
||||||
|
} else if (j.is_object()) {
|
||||||
|
exPolygons.push_back(j.get<ExPolygon>());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error("Invalid ExPolygons JSON format. Expected array of ExPolygons.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return exPolygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save ExPolygons to a file
|
||||||
|
void dumpExPolygonsToTxt(const std::vector<ExPolygon> &exPolygons, const std::string &filePath)
|
||||||
|
{
|
||||||
|
std::ofstream file(filePath);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Cannot open file for writing: " << filePath << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < exPolygons.size(); ++i) {
|
||||||
|
const auto &exPolygon = exPolygons[i];
|
||||||
|
file << "# ExPolygon " << i + 1 << "\n";
|
||||||
|
|
||||||
|
// Save the outer contour
|
||||||
|
file << "contour:";
|
||||||
|
for (const auto &point : exPolygon.contour) { file << " " << point.x() << " " << point.y(); }
|
||||||
|
file << "\n";
|
||||||
|
|
||||||
|
// Save the holes
|
||||||
|
for (const auto &hole : exPolygon.holes) {
|
||||||
|
file << "hole:";
|
||||||
|
for (const auto &point : hole) { file << " " << point.x() << " " << point.y(); }
|
||||||
|
file << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
std::cout << "ExPolygons saved to " << filePath << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load ExPolygons from a file
|
||||||
|
std::vector<ExPolygon> loadExPolygonsFromTxt(const std::string &filePath)
|
||||||
|
{
|
||||||
|
std::vector<ExPolygon> exPolygons;
|
||||||
|
|
||||||
|
std::ifstream file(filePath);
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Error: Cannot open file for reading: " << filePath << std::endl;
|
||||||
|
return exPolygons;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
ExPolygon currentPolygon;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
if (line.empty() || line[0] == '#') {
|
||||||
|
// Start of a new polygon
|
||||||
|
if (!currentPolygon.contour.empty() || !currentPolygon.holes.empty()) {
|
||||||
|
exPolygons.push_back(currentPolygon);
|
||||||
|
currentPolygon = ExPolygon();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istringstream stream(line);
|
||||||
|
std::string keyword;
|
||||||
|
stream >> keyword;
|
||||||
|
|
||||||
|
if (keyword == "contour:") {
|
||||||
|
currentPolygon.contour.clear();
|
||||||
|
coord_t x, y;
|
||||||
|
while (stream >> x >> y) { currentPolygon.contour.append({x, y}); }
|
||||||
|
} else if (keyword == "hole:") {
|
||||||
|
Polygon hole;
|
||||||
|
coord_t x, y;
|
||||||
|
while (stream >> x >> y) { hole.append({x, y}); }
|
||||||
|
currentPolygon.holes.push_back(hole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the last polygon if any
|
||||||
|
if (!currentPolygon.contour.empty() || !currentPolygon.holes.empty()) { exPolygons.push_back(currentPolygon); }
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
std::cout << "Loaded " << exPolygons.size() << " ExPolygons from " << filePath << std::endl;
|
||||||
|
return exPolygons;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,8 @@ public:
|
||||||
void draw_grid(const BoundingBox& bbox, const std::string& stroke = "black", coordf_t stroke_width = scale_(0.05), coordf_t step=scale_(1.0));
|
void draw_grid(const BoundingBox& bbox, const std::string& stroke = "black", coordf_t stroke_width = scale_(0.05), coordf_t step=scale_(1.0));
|
||||||
void add_comment(const std::string comment);
|
void add_comment(const std::string comment);
|
||||||
|
|
||||||
|
static ExPolygons load(const std::string& filename);
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -177,6 +179,12 @@ private:
|
||||||
float to_svg_y(float x) const throw() { return flipY ? this->height - to_svg_coord(x) : to_svg_coord(x); }
|
float to_svg_y(float x) const throw() { return flipY ? this->height - to_svg_coord(x) : to_svg_coord(x); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void dumpExPolygonToJson(const ExPolygon &exPolygon, const std::string &filePath);
|
||||||
|
void dumpExPolygonsToJson(const std::vector<ExPolygon> &exPolygons, const std::string &filePath);
|
||||||
|
std::vector<ExPolygon> loadExPolygonsFromJson(const std::string &filePath);
|
||||||
|
|
||||||
|
void dumpExPolygonsToTxt(const std::vector<ExPolygon> &exPolygons, const std::string &filePath);
|
||||||
|
std::vector<ExPolygon> loadExPolygonsFromTxt(const std::string &filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
|
#include "Clipper2Utils.hpp"
|
||||||
#include "Fill/FillBase.hpp"
|
#include "Fill/FillBase.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "Layer.hpp"
|
#include "Layer.hpp"
|
||||||
|
@ -629,6 +630,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
|
||||||
SVG svg(debug_out_path("machine_boarder.svg"), m_object->bounding_box());
|
SVG svg(debug_out_path("machine_boarder.svg"), m_object->bounding_box());
|
||||||
if (svg.is_opened()) svg.draw(m_machine_border, "yellow");
|
if (svg.is_opened()) svg.draw(m_machine_border, "yellow");
|
||||||
#endif
|
#endif
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "tree support construct finish";
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_overhang(Layer *layer, const ExPolygon &overhang, int type)
|
void add_overhang(Layer *layer, const ExPolygon &overhang, int type)
|
||||||
|
@ -3871,14 +3873,16 @@ TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance
|
||||||
m_max_move_distances.resize(object.layers().size(), 0);
|
m_max_move_distances.resize(object.layers().size(), 0);
|
||||||
m_layer_outlines.resize(object.layers().size());
|
m_layer_outlines.resize(object.layers().size());
|
||||||
m_layer_outlines_below.resize(object.layer_count());
|
m_layer_outlines_below.resize(object.layer_count());
|
||||||
for (std::size_t layer_nr = 0; layer_nr < object.layers().size(); ++layer_nr)
|
for (std::size_t layer_nr = 0; layer_nr < object.layers().size(); ++layer_nr) {
|
||||||
{
|
// BOOST_LOG_TRIVIAL(debug) << "TreeSupportData construct "<< layer_nr<<"/"<<object.layer_count();
|
||||||
const Layer* layer = object.get_layer(layer_nr);
|
const Layer* layer = object.get_layer(layer_nr);
|
||||||
m_max_move_distances[layer_nr] = layer->height * branch_scale_factor;
|
m_max_move_distances[layer_nr] = layer->height * branch_scale_factor;
|
||||||
ExPolygons &outline = m_layer_outlines[layer_nr];
|
ExPolygons &outline = m_layer_outlines[layer_nr];
|
||||||
for (const ExPolygon& poly : layer->lslices) {
|
outline.clear();
|
||||||
poly.simplify(scale_(m_radius_sample_resolution), &outline);
|
outline.reserve(layer->lslices.size());
|
||||||
}
|
for (const ExPolygon &poly : layer->lslices) { append(outline, to_expolygons( poly.simplify_p(scale_(m_radius_sample_resolution)))); }
|
||||||
|
if (layer_nr % 10 == 0)
|
||||||
|
outline = union_ex(outline);
|
||||||
|
|
||||||
if (layer_nr == 0)
|
if (layer_nr == 0)
|
||||||
m_layer_outlines_below[layer_nr] = outline;
|
m_layer_outlines_below[layer_nr] = outline;
|
||||||
|
@ -3886,7 +3890,7 @@ TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance
|
||||||
m_layer_outlines_below[layer_nr] = m_layer_outlines_below[layer_nr - 1];
|
m_layer_outlines_below[layer_nr] = m_layer_outlines_below[layer_nr - 1];
|
||||||
m_layer_outlines_below[layer_nr].insert(m_layer_outlines_below[layer_nr].end(), outline.begin(), outline.end());
|
m_layer_outlines_below[layer_nr].insert(m_layer_outlines_below[layer_nr].end(), outline.begin(), outline.end());
|
||||||
if (layer_nr%10==0)
|
if (layer_nr%10==0)
|
||||||
m_layer_outlines_below[layer_nr] = union_ex(m_layer_outlines_below[layer_nr]);
|
m_layer_outlines_below[layer_nr] = union_ex2(m_layer_outlines_below[layer_nr]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue