mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 22:45:58 +08:00
Start using the new interface
This commit is contained in:
parent
614ab6bdb9
commit
a52a4bcbb2
@ -3,180 +3,122 @@
|
|||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/TriangleMesh.hpp"
|
#include "libslic3r/TriangleMesh.hpp"
|
||||||
|
|
||||||
#include "boost/algorithm/string/split.hpp"
|
|
||||||
#include "boost/filesystem/path.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "libseqarrange/include/seq_interface.hpp"
|
||||||
|
#include "libseqarrange/include/seq_sequential.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
static Sequential::PrinterGeometry get_printer_geometry() {
|
||||||
|
enum ShapeType {
|
||||||
|
BOX,
|
||||||
|
CONVEX
|
||||||
|
};
|
||||||
|
struct ExtruderSlice {
|
||||||
|
coord_t height;
|
||||||
|
ShapeType shape_type;
|
||||||
|
std::vector<Polygon> polygons;
|
||||||
|
|
||||||
static bool find_and_remove(std::string& src, const std::string& key)
|
};
|
||||||
{
|
|
||||||
size_t pos = src.find(key);
|
// Just hardcode MK4 geometry for now.
|
||||||
if (pos != std::string::npos) {
|
std::vector<ExtruderSlice> slices;
|
||||||
src.erase(pos, key.length());
|
slices.push_back(ExtruderSlice{ 0, CONVEX, { { { -500000, -500000 }, { 500000, -500000 }, { 500000, 500000 }, { -500000, 500000 } } } });
|
||||||
return true;
|
slices.push_back(ExtruderSlice{ 3000000, CONVEX, { { { -1000000, -21000000 }, { 37000000, -21000000 }, { 37000000, 44000000 }, { -1000000, 44000000 } },
|
||||||
|
{ { -40000000, -45000000 }, { 38000000, -45000000 }, { 38000000, 20000000 }, { -40000000, 20000000 } } } });
|
||||||
|
slices.push_back(ExtruderSlice{ 11000000, BOX, { { {-350000000, -4000000 }, {350000000, -4000000 }, {350000000, -14000000 }, {-350000000, -14000000 } } } });
|
||||||
|
slices.push_back(ExtruderSlice{ 13000000, BOX, { { { -12000000, -350000000 }, { 9000000, -350000000 }, { 9000000, -39000000 }, { -12000000, -39000000 } },
|
||||||
|
{ { -12000000, -350000000 }, {250000000, -350000000 }, {250000000, -82000000 }, { -12000000, -82000000} } } });
|
||||||
|
|
||||||
|
Sequential::PrinterGeometry out;
|
||||||
|
out.x_size = 250000000;
|
||||||
|
out.y_size = 210000000;
|
||||||
|
for (const ExtruderSlice& slice : slices) {
|
||||||
|
(slice.shape_type == CONVEX ? out.convex_heights : out.box_heights).emplace(slice.height);
|
||||||
|
out.extruder_slices.insert(std::make_pair(slice.height, slice.polygons));
|
||||||
}
|
}
|
||||||
return false;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ObjectToPrint {
|
static Sequential::SolverConfiguration get_solver_config(const Sequential::PrinterGeometry& printer_geometry)
|
||||||
int id = 0;
|
|
||||||
coord_t total_height = 0;
|
|
||||||
std::vector<std::pair<coord_t, Polygon>> pgns_at_height;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<ObjectToPrint> load_exported_data(const std::string& filename)
|
|
||||||
{
|
{
|
||||||
std::vector<ObjectToPrint> objects_to_print;
|
return Sequential::SolverConfiguration(printer_geometry);
|
||||||
|
|
||||||
std::ifstream in(filename);
|
|
||||||
if (! in)
|
|
||||||
throw std::runtime_error("NO EXPORTED FILE WAS FOUND");
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
while (in) {
|
|
||||||
std::getline(in, line);
|
|
||||||
if (find_and_remove(line, "OBJECT_ID")) {
|
|
||||||
objects_to_print.push_back(ObjectToPrint());
|
|
||||||
objects_to_print.back().id = std::stoi(line);
|
|
||||||
}
|
|
||||||
if (find_and_remove(line, "TOTAL_HEIGHT"))
|
|
||||||
objects_to_print.back().total_height = std::stoi(line);
|
|
||||||
if (find_and_remove(line, "POLYGON_AT_HEIGHT"))
|
|
||||||
objects_to_print.back().pgns_at_height.emplace_back(std::make_pair(std::stoi(line), Polygon()));
|
|
||||||
if (find_and_remove(line, "POINT")) {
|
|
||||||
std::stringstream ss(line);
|
|
||||||
std::string val;
|
|
||||||
ss >> val;
|
|
||||||
Point pt(std::stoi(val), 0);
|
|
||||||
ss >> val;
|
|
||||||
pt.y() = std::stoi(val);
|
|
||||||
objects_to_print.back().pgns_at_height.back().second.append(pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return objects_to_print;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<double> read_required_heights()
|
static std::vector<Sequential::ObjectToPrint> get_objects_to_print(const Model& model, const Sequential::PrinterGeometry& printer_geometry)
|
||||||
{
|
{
|
||||||
|
// First extract the heights of interest.
|
||||||
std::vector<double> heights;
|
std::vector<double> heights;
|
||||||
std::ifstream out("printer_geometry.mk4.txt");
|
for (const auto& [height, pgns] : printer_geometry.extruder_slices)
|
||||||
std::string line;
|
heights.push_back(unscaled(height));
|
||||||
std::array<std::string, 2> keys = {"CONVEX_HEIGHT", "BOX_HEIGHT"};
|
Slic3r::sort_remove_duplicates(heights);
|
||||||
while (out) {
|
|
||||||
std::getline(out, line);
|
|
||||||
for (const std::string& key : keys) {
|
|
||||||
if (size_t pos = line.find(key); pos != std::string::npos) {
|
|
||||||
line = line.substr(pos + key.size());
|
|
||||||
heights.push_back(double(std::stoi(line)) * SCALING_FACTOR);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return heights;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Now collect all objects and projections of convex hull above respective heights.
|
||||||
|
std::vector<Sequential::ObjectToPrint> objects;
|
||||||
void export_arrange_data(const Model& model, const std::vector<double>& heights)
|
|
||||||
{
|
|
||||||
std::ofstream out("arrange_data_export.txt");
|
|
||||||
for (const ModelObject* mo : model.objects) {
|
for (const ModelObject* mo : model.objects) {
|
||||||
// Calculate polygon describing convex hull of everything above given heights.
|
|
||||||
const ModelInstance* mi = mo->instances.front();
|
const ModelInstance* mi = mo->instances.front();
|
||||||
out << "OBJECT_ID" << mo->id().id << std::endl;
|
objects.emplace_back(Sequential::ObjectToPrint{int(mo->id().id), scaled(mo->instance_bounding_box(0).size().z()), {}});
|
||||||
out << "TOTAL_HEIGHT" << scaled(mo->instance_bounding_box(0).size().z()) << std::endl;;
|
|
||||||
for (double height : heights) {
|
for (double height : heights) {
|
||||||
auto tr = Transform3d::Identity();
|
auto tr = Transform3d::Identity();
|
||||||
Vec3d offset = mi->get_offset();
|
Vec3d offset = mi->get_offset();
|
||||||
tr.translate(Vec3d(-offset.x(), -offset.y(), 0.));
|
tr.translate(Vec3d(-offset.x(), -offset.y(), 0.));
|
||||||
Polygon pgn = its_convex_hull_2d_above(mo->mesh().its, tr.cast<float>(), height);
|
Polygon pgn = its_convex_hull_2d_above(mo->mesh().its, tr.cast<float>(), height);
|
||||||
out << "POLYGON_AT_HEIGHT" << scaled(height) << std::endl;
|
objects.back().pgns_at_height.emplace_back(std::make_pair(scaled(height), pgn));
|
||||||
for (const Point& pt : pgn)
|
|
||||||
out << "POINT" << pt.x() << " " << pt.y() << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void import_arrange_data(Model& model, bool delete_files)
|
void arrange_model_sequential(Model& model)
|
||||||
{
|
{
|
||||||
// First go through all files in the current directory
|
Sequential::PrinterGeometry printer_geometry = get_printer_geometry();
|
||||||
// and remember all which match the pattern.
|
Sequential::SolverConfiguration solver_config = get_solver_config(printer_geometry);
|
||||||
namespace fs = boost::filesystem;
|
std::vector<Sequential::ObjectToPrint> objects = get_objects_to_print(model, printer_geometry);
|
||||||
fs::path p(".");
|
|
||||||
fs::directory_iterator end_itr;
|
// Everything ready - let libseqarrange do the actual arrangement.
|
||||||
std::vector<std::string> filenames;
|
std::vector<Sequential::ScheduledPlate> plates =
|
||||||
for (fs::directory_iterator itr(p); itr != end_itr; ++itr)
|
Sequential::schedule_ObjectsForSequentialPrint(
|
||||||
{
|
solver_config,
|
||||||
if (fs::is_regular_file(itr->path())) {
|
printer_geometry,
|
||||||
std::string name = itr->path().filename().string();
|
objects);
|
||||||
if (boost::starts_with(name, "arrange_data_import") && boost::ends_with(name, ".txt"))
|
|
||||||
filenames.emplace_back(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Sort the files alphabetically.
|
|
||||||
std::sort(filenames.begin(), filenames.end());
|
|
||||||
|
|
||||||
|
|
||||||
|
// Extract the result and move the objects in Model accordingly.
|
||||||
struct MoveData {
|
struct MoveData {
|
||||||
size_t id;
|
Sequential::ScheduledObject scheduled_object;
|
||||||
coord_t x;
|
|
||||||
coord_t y;
|
|
||||||
size_t bed_idx;
|
size_t bed_idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A vector to collect move data for all the files.
|
// A vector to collect move data for all the objects.
|
||||||
std::vector<MoveData> move_data_all;
|
std::vector<MoveData> move_data_all;
|
||||||
|
|
||||||
|
|
||||||
// Now iterate through all the files, read the data and move the objects accordingly.
|
// Now iterate through all the files, read the data and move the objects accordingly.
|
||||||
// Save the move data from this file to move_data_all.
|
// Save the move data from this file to move_data_all.
|
||||||
size_t bed_idx = 0;
|
size_t bed_idx = 0;
|
||||||
for (const std::string& name : filenames) {
|
for (const Sequential::ScheduledPlate& plate : plates) {
|
||||||
std::cout << " - loading file " << name << "..." << std::endl;
|
|
||||||
std::ifstream in(name);
|
|
||||||
|
|
||||||
std::string line;
|
|
||||||
std::vector<MoveData> move_data;
|
|
||||||
while (in) {
|
|
||||||
std::getline(in, line);
|
|
||||||
std::vector<std::string> values;
|
|
||||||
boost::split(values, line, boost::is_any_of(" "));
|
|
||||||
if (values.size() > 2)
|
|
||||||
move_data.emplace_back(MoveData{size_t(std::stoi(values[0])), std::stoi(values[1]), std::stoi(values[2]), bed_idx});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate the same way as when exporting.
|
// Iterate the same way as when exporting.
|
||||||
for (ModelObject* mo : model.objects) {
|
for (ModelObject* mo : model.objects) {
|
||||||
ModelInstance* mi = mo->instances.front();
|
ModelInstance* mi = mo->instances.front();
|
||||||
const ObjectID& oid = mo->id();
|
const ObjectID& oid = mo->id();
|
||||||
auto it = std::find_if(move_data.begin(), move_data.end(), [&oid](const auto& md) { return md.id == oid.id; });
|
auto it = std::find_if(plate.scheduled_objects.begin(), plate.scheduled_objects.end(), [&oid](const auto& md) { return md.id == oid.id; });
|
||||||
if (it != move_data.end()) {
|
if (it != plate.scheduled_objects.end()) {
|
||||||
mi->set_offset(Vec3d(unscaled(it->x) + bed_idx * 300, unscaled(it->y), mi->get_offset().z()));
|
mi->set_offset(Vec3d(unscaled(it->x) + bed_idx * 300, unscaled(it->y), mi->get_offset().z()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
move_data_all.insert(move_data_all.end(), move_data.begin(), move_data.end());
|
for (const Sequential::ScheduledObject& object : plate.scheduled_objects)
|
||||||
|
move_data_all.push_back({ object, bed_idx });
|
||||||
++bed_idx;
|
++bed_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delete_files) {
|
|
||||||
std::cout << " - removing all the files...";
|
|
||||||
for (const std::string& name : filenames)
|
|
||||||
fs::remove(fs::path(name));
|
|
||||||
std::cout << "done" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now reorder the objects in the model so they are in the same order as requested.
|
// Now reorder the objects in the model so they are in the same order as requested.
|
||||||
auto comp = [&move_data_all](ModelObject* mo1, ModelObject* mo2) {
|
auto comp = [&move_data_all](ModelObject* mo1, ModelObject* mo2) {
|
||||||
auto it1 = std::find_if(move_data_all.begin(), move_data_all.end(), [&mo1](const auto& md) { return md.id == mo1->id().id; });
|
auto it1 = std::find_if(move_data_all.begin(), move_data_all.end(), [&mo1](const auto& md) { return md.scheduled_object.id == mo1->id().id; });
|
||||||
auto it2 = std::find_if(move_data_all.begin(), move_data_all.end(), [&mo2](const auto& md) { return md.id == mo2->id().id; });
|
auto it2 = std::find_if(move_data_all.begin(), move_data_all.end(), [&mo2](const auto& md) { return md.scheduled_object.id == mo2->id().id; });
|
||||||
return it1->bed_idx == it2->bed_idx ? it1 < it2 : it1->bed_idx < it2->bed_idx;
|
return it1->bed_idx == it2->bed_idx ? it1 < it2 : it1->bed_idx < it2->bed_idx;
|
||||||
};
|
};
|
||||||
std::sort(model.objects.begin(), model.objects.end(), comp);
|
std::sort(model.objects.begin(), model.objects.end(), comp);
|
||||||
@ -184,35 +126,21 @@ void import_arrange_data(Model& model, bool delete_files)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void export_run_and_import_arrange_data(Model& model, const std::string& cmd, bool delete_files)
|
bool check_seq_printability(const Model& model)
|
||||||
{
|
{
|
||||||
std::cout << "Reading height from printer_geometry.mk4.txt" << std::endl;
|
Sequential::PrinterGeometry printer_geometry = get_printer_geometry();
|
||||||
std::vector<double> heights = read_required_heights();
|
Sequential::SolverConfiguration solver_config = get_solver_config(printer_geometry);
|
||||||
if (heights.empty()) {
|
std::vector<Sequential::ObjectToPrint> objects = get_objects_to_print(model, printer_geometry);
|
||||||
std::cout << "unable" << std::endl;
|
|
||||||
return;
|
// FIXME: This does not consider plates, non-printable objects and instances.
|
||||||
|
Sequential::ScheduledPlate plate;
|
||||||
|
for (ModelObject* mo : model.objects) {
|
||||||
|
ModelInstance* mi = mo->instances.front();
|
||||||
|
plate.scheduled_objects.emplace_back(mo->id().id, scaled(mi->get_offset().x()), scaled(mi->get_offset().y()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Exporting the arrange data...";
|
return Sequential::check_ScheduledObjectsForSequentialPrintability(solver_config, printer_geometry, objects, std::vector<Sequential::ScheduledPlate>(1, plate));
|
||||||
export_arrange_data(model, heights);
|
|
||||||
std::cout << "done" << std::endl;
|
|
||||||
|
|
||||||
std::cout << "Running " << cmd << std::endl;
|
|
||||||
int out = wxExecute(wxString::FromUTF8(cmd), wxEXEC_SYNC);
|
|
||||||
if (out == -1) {
|
|
||||||
std::cout << "unable" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::cout << "sequential_prusa returned " << out << (out == 0 ? ": ok" : ": appears to be an error") << std::endl;
|
|
||||||
|
|
||||||
if (out ==0 ) {
|
|
||||||
std::cout << "Importing the arrange data..." << std::endl;
|
|
||||||
import_arrange_data(model, delete_files);
|
|
||||||
std::cout << "Import done" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -4,9 +4,8 @@
|
|||||||
class Model;
|
class Model;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
void export_run_and_import_arrange_data(Model&, const std::string&, bool);
|
void arrange_model_sequential(Model& model);
|
||||||
void export_arrange_data(const Model& model, const std::vector<double>& heights);
|
bool check_seq_printability(const Model& model);
|
||||||
void import_arrange_data(Model& model, bool delete_files);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // slic3r_Arrange_Helper_hpp
|
#endif // slic3r_Arrange_Helper_hpp
|
@ -2224,39 +2224,29 @@ void GLCanvas3D::render()
|
|||||||
// This is just temporary pipe to export data to the separate arrange algorithm
|
// This is just temporary pipe to export data to the separate arrange algorithm
|
||||||
// and importing the result back. TESTING ONLY !!!
|
// and importing the result back. TESTING ONLY !!!
|
||||||
ImGui::Begin("TESTING ONLY (arrange)");
|
ImGui::Begin("TESTING ONLY (arrange)");
|
||||||
static bool decimation = true;
|
if (ImGui::Button("Do sequential arrange")) {
|
||||||
static bool precision_high = false;
|
arrange_model_sequential(wxGetApp().plater()->model());
|
||||||
static bool assumptions = false;
|
|
||||||
static int object_group_size = 4;
|
|
||||||
static bool delete_files = true;
|
|
||||||
static std::string printer_file = "printer_geometry.mk4.txt";
|
|
||||||
ImGui::Checkbox("Decimation", &decimation);
|
|
||||||
ImGui::Checkbox("Precision", &precision_high);
|
|
||||||
ImGui::Checkbox("Assumptions", &assumptions);
|
|
||||||
ImGui::InputInt("Group size", &object_group_size);
|
|
||||||
wxGetApp().imgui()->disabled_begin(true);
|
|
||||||
ImGui::InputText("Printer file", printer_file.data(), printer_file.size());
|
|
||||||
wxGetApp().imgui()->disabled_end();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Checkbox("Delete files after use", &delete_files);
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "./sequential_prusa"
|
|
||||||
<< " --decimation=" << (decimation ? "yes" : "no")
|
|
||||||
<< " --precision=" << (precision_high ? "high" : "low")
|
|
||||||
<< " --assumptions=" << (assumptions ? "yes" : "no")
|
|
||||||
<< " --object-group-size=" << object_group_size
|
|
||||||
<< " --printer-file=" << printer_file
|
|
||||||
<< " --interactive=no";
|
|
||||||
ImGui::Text((std::string("Command: '") + ss.str() + "'").c_str());
|
|
||||||
|
|
||||||
|
|
||||||
if (ImGui::Button("Do external arrange")) {
|
|
||||||
export_run_and_import_arrange_data(wxGetApp().plater()->model(), ss.str(), delete_files);
|
|
||||||
reload_scene(true, true);
|
reload_scene(true, true);
|
||||||
wxGetApp().obj_list()->update_after_undo_redo();
|
wxGetApp().obj_list()->update_after_undo_redo();
|
||||||
}
|
}
|
||||||
ImGui::End();
|
|
||||||
|
static auto time_start = std::chrono::high_resolution_clock::now();
|
||||||
|
auto time_now = std::chrono::high_resolution_clock::now();
|
||||||
|
int time_limit_s = 1;
|
||||||
|
static bool last_res = 0;
|
||||||
|
bool valid = std::chrono::duration_cast<std::chrono::seconds>(time_now - time_start).count() < time_limit_s;
|
||||||
|
|
||||||
|
ImGui::Text("");
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("");
|
||||||
|
if (ImGui::Button("Test:")) {
|
||||||
|
last_res = check_seq_printability(wxGetApp().plater()->model());
|
||||||
|
time_start = std::chrono::high_resolution_clock::now();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextColored((valid && last_res ? ImVec4(0.,1.,0.,1.) : (valid ? ImVec4(1.,0.,0.,1.) : ImVec4(0.5,.5,0.5,1.))) , "\u25a0");
|
||||||
|
ImGui::Text("(So far, multiple beds are not accounted for.)");
|
||||||
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user