mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-17 10:32:20 +00:00
Port STEP import dialog from BambuStudio (#9102)
 Thanks BambuLab! Fix #8820
This commit is contained in:
@@ -1386,7 +1386,7 @@ int CLI::run(int argc, char **argv)
|
||||
// BBS: adjust whebackup
|
||||
//LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances;
|
||||
//if (load_aux) strategy = strategy | LoadStrategy::LoadAuxiliary;
|
||||
model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, nullptr, nullptr, plate_to_slice);
|
||||
model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, plate_to_slice);
|
||||
if (is_bbl_3mf)
|
||||
{
|
||||
if (!first_file)
|
||||
|
||||
@@ -411,6 +411,17 @@ void AppConfig::set_defaults()
|
||||
set_str("print", "timelapse", "1");
|
||||
}
|
||||
|
||||
if (get("enable_step_mesh_setting").empty()) {
|
||||
set_bool("enable_step_mesh_setting", true);
|
||||
}
|
||||
if (get("linear_defletion", "angle_defletion").empty()) {
|
||||
set("linear_defletion", "0.003");
|
||||
set("angle_defletion", "0.5");
|
||||
}
|
||||
if (get("is_split_compound").empty()) {
|
||||
set_bool("is_split_compound", false);
|
||||
}
|
||||
|
||||
// Remove legacy window positions/sizes
|
||||
erase("app", "main_frame_maximized");
|
||||
erase("app", "main_frame_pos");
|
||||
|
||||
@@ -33,9 +33,8 @@
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "TopExp_Explorer.hxx"
|
||||
#include "BRep_Tool.hxx"
|
||||
|
||||
const double STEP_TRANS_CHORD_ERROR = 0.003;
|
||||
const double STEP_TRANS_ANGLE_RES = 0.5;
|
||||
#include "BRepTools.hxx"
|
||||
#include <IMeshTools_Parameters.hxx>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -166,26 +165,24 @@ int StepPreProcessor::preNum(const unsigned char byte) {
|
||||
return num;
|
||||
}
|
||||
|
||||
struct NamedSolid {
|
||||
NamedSolid(const TopoDS_Shape& s,
|
||||
const std::string& n) : solid{s}, name{n} {}
|
||||
const TopoDS_Shape solid;
|
||||
const std::string name;
|
||||
};
|
||||
|
||||
static void getNamedSolids(const TopLoc_Location& location, const std::string& prefix,
|
||||
unsigned int& id, const Handle(XCAFDoc_ShapeTool) shapeTool,
|
||||
const TDF_Label label, std::vector<NamedSolid>& namedSolids) {
|
||||
static void getNamedSolids(const TopLoc_Location& location,
|
||||
const std::string& prefix,
|
||||
unsigned int& id,
|
||||
const Handle(XCAFDoc_ShapeTool) shapeTool,
|
||||
const TDF_Label label,
|
||||
std::vector<NamedSolid>& namedSolids,
|
||||
bool isSplitCompound = false) {
|
||||
TDF_Label referredLabel{label};
|
||||
if (shapeTool->IsReference(label))
|
||||
shapeTool->GetReferredShape(label, referredLabel);
|
||||
|
||||
std::string name;
|
||||
Handle(TDataStd_Name) shapeName;
|
||||
if (referredLabel.FindAttribute(TDataStd_Name::GetID(), shapeName))
|
||||
if (referredLabel.FindAttribute(TDataStd_Name::GetID(), shapeName) ||
|
||||
label.FindAttribute(TDataStd_Name::GetID(), shapeName))
|
||||
name = TCollection_AsciiString(shapeName->Get()).ToCString();
|
||||
|
||||
if (name == "")
|
||||
if (name == "" || !StepPreProcessor::isUtf8(name))
|
||||
name = std::to_string(id++);
|
||||
std::string fullName{name};
|
||||
|
||||
@@ -193,7 +190,7 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
||||
TDF_LabelSequence components;
|
||||
if (shapeTool->GetComponents(referredLabel, components)) {
|
||||
for (Standard_Integer compIndex = 1; compIndex <= components.Length(); ++compIndex) {
|
||||
getNamedSolids(localLocation, fullName, id, shapeTool, components.Value(compIndex), namedSolids);
|
||||
getNamedSolids(localLocation, fullName, id, shapeTool, components.Value(compIndex), namedSolids, isSplitCompound);
|
||||
}
|
||||
} else {
|
||||
TopoDS_Shape shape;
|
||||
@@ -204,12 +201,20 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
||||
int i = 0;
|
||||
switch (shape_type) {
|
||||
case TopAbs_COMPOUND:
|
||||
if (!isSplitCompound) {
|
||||
namedSolids.emplace_back(TopoDS::Compound(transform.Shape()), fullName);
|
||||
break;
|
||||
}
|
||||
case TopAbs_COMPSOLID:
|
||||
for (explorer.Init(transform.Shape(), TopAbs_SOLID); explorer.More(); explorer.Next()) {
|
||||
i++;
|
||||
const TopoDS_Shape& currentShape = explorer.Current();
|
||||
namedSolids.emplace_back(TopoDS::Solid(currentShape), fullName + "-SOLID-" + std::to_string(i));
|
||||
}
|
||||
if (!isSplitCompound) {
|
||||
namedSolids.emplace_back(TopoDS::CompSolid(transform.Shape()), fullName);
|
||||
} else {
|
||||
for (explorer.Init(transform.Shape(), TopAbs_SOLID); explorer.More(); explorer.Next()) {
|
||||
i++;
|
||||
const TopoDS_Shape& currentShape = explorer.Current();
|
||||
namedSolids.emplace_back(TopoDS::Solid(currentShape), fullName + "-SOLID-" + std::to_string(i));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TopAbs_SOLID:
|
||||
namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), fullName);
|
||||
@@ -223,7 +228,11 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
||||
}
|
||||
}
|
||||
|
||||
bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn)
|
||||
bool load_step(const char *path, Model *model, bool& is_cancel,
|
||||
double linear_defletion/*=0.003*/,
|
||||
double angle_defletion/*= 0.5*/,
|
||||
bool isSplitCompound,
|
||||
ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn, long& mesh_face_num)
|
||||
{
|
||||
bool cb_cancel = false;
|
||||
if (stepFn) {
|
||||
@@ -271,14 +280,14 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre
|
||||
return false;
|
||||
}
|
||||
}
|
||||
getNamedSolids(TopLoc_Location{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids);
|
||||
getNamedSolids(TopLoc_Location{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids, isSplitCompound);
|
||||
}
|
||||
|
||||
std::vector<stl_file> stl;
|
||||
stl.resize(namedSolids.size());
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, namedSolids.size()), [&](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t i = range.begin(); i < range.end(); i++) {
|
||||
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
|
||||
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, linear_defletion, false, angle_defletion, true);
|
||||
// BBS: calculate total number of the nodes and triangles
|
||||
int aNbNodes = 0;
|
||||
int aNbTriangles = 0;
|
||||
@@ -324,7 +333,7 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre
|
||||
}
|
||||
// BBS: copy triangles
|
||||
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
|
||||
Standard_Integer anId[3];
|
||||
Standard_Integer anId[3] = {};
|
||||
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
|
||||
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
|
||||
|
||||
@@ -351,6 +360,14 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre
|
||||
}
|
||||
});
|
||||
|
||||
if (mesh_face_num != -1) {
|
||||
for (size_t i = 0; i < stl.size(); i++) {
|
||||
// Test for overflow
|
||||
mesh_face_num += stl[i].stats.number_of_facets;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ModelObject *new_object = model->add_object();
|
||||
const char * last_slash = strrchr(path, DIR_SEPARATOR);
|
||||
new_object->name.assign((last_slash == nullptr) ? path : last_slash + 1);
|
||||
@@ -395,4 +412,109 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre
|
||||
return true;
|
||||
}
|
||||
|
||||
Step::Step(fs::path path, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn):
|
||||
m_stepFn(stepFn),
|
||||
m_utf8Fn(isUtf8Fn)
|
||||
{
|
||||
m_path = path.string();
|
||||
m_app->NewDocument(TCollection_ExtendedString("BinXCAF"), m_doc);
|
||||
}
|
||||
|
||||
Step::Step(std::string path, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn) :
|
||||
m_path(path),
|
||||
m_stepFn(stepFn),
|
||||
m_utf8Fn(isUtf8Fn)
|
||||
{
|
||||
m_app->NewDocument(TCollection_ExtendedString("BinXCAF"), m_doc);
|
||||
}
|
||||
|
||||
bool Step::load()
|
||||
{
|
||||
if (!StepPreProcessor::isUtf8File(m_path.c_str()) && m_utf8Fn) {
|
||||
m_utf8Fn(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEPCAFControl_Reader reader;
|
||||
reader.SetNameMode(true);
|
||||
IFSelect_ReturnStatus stat = reader.ReadFile(m_path.c_str());
|
||||
if (stat != IFSelect_RetDone || !reader.Transfer(m_doc)) {
|
||||
m_app->Close(m_doc);
|
||||
return false;
|
||||
}
|
||||
m_shape_tool = XCAFDoc_DocumentTool::ShapeTool(m_doc->Main());
|
||||
TDF_LabelSequence topLevelShapes;
|
||||
m_shape_tool->GetFreeShapes(topLevelShapes);
|
||||
unsigned int id{ 1 };
|
||||
Standard_Integer topShapeLength = topLevelShapes.Length() + 1;
|
||||
for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) {
|
||||
getNamedSolids(TopLoc_Location{}, "", id, m_shape_tool, topLevelShapes.Value(iLabel), m_name_solids);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Step::clean_mesh_data()
|
||||
{
|
||||
for (const auto& name_solid : m_name_solids) {
|
||||
BRepTools::Clean(name_solid.solid);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Step::get_triangle_num(double linear_defletion, double angle_defletion)
|
||||
{
|
||||
unsigned int tri_num = 0;
|
||||
try {
|
||||
Handle(StepProgressIncdicator) progress = new StepProgressIncdicator(m_stop_mesh);
|
||||
clean_mesh_data();
|
||||
IMeshTools_Parameters param;
|
||||
param.Deflection = linear_defletion;
|
||||
param.Angle = angle_defletion;
|
||||
param.InParallel = true;
|
||||
for (int i = 0; i < m_name_solids.size(); ++i) {
|
||||
BRepMesh_IncrementalMesh mesh(m_name_solids[i].solid, param, progress->Start());
|
||||
for (TopExp_Explorer anExpSF(m_name_solids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
|
||||
TopLoc_Location aLoc;
|
||||
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
|
||||
if (!aTriangulation.IsNull()) {
|
||||
tri_num += aTriangulation->NbTriangles();
|
||||
}
|
||||
}
|
||||
if (m_stop_mesh.load()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tri_num;
|
||||
}
|
||||
|
||||
unsigned int Step::get_triangle_num_tbb(double linear_defletion, double angle_defletion)
|
||||
{
|
||||
unsigned int tri_num = 0;
|
||||
clean_mesh_data();
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_name_solids.size()),
|
||||
[&](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t i = range.begin(); i < range.end(); i++) {
|
||||
unsigned int solids_tri_num = 0;
|
||||
BRepMesh_IncrementalMesh mesh(m_name_solids[i].solid, linear_defletion, false, angle_defletion, true);
|
||||
for (TopExp_Explorer anExpSF(m_name_solids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
|
||||
TopLoc_Location aLoc;
|
||||
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
|
||||
if (!aTriangulation.IsNull()) {
|
||||
solids_tri_num += aTriangulation->NbTriangles();
|
||||
}
|
||||
}
|
||||
m_name_solids[i].tri_face_cout = solids_tri_num;
|
||||
}
|
||||
|
||||
});
|
||||
for (int i = 0; i < m_name_solids.size(); ++i) {
|
||||
tri_num += m_name_solids[i].tri_face_cout;
|
||||
}
|
||||
return tri_num;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
#ifndef slic3r_Format_STEP_hpp_
|
||||
#define slic3r_Format_STEP_hpp_
|
||||
#include "XCAFDoc_DocumentTool.hxx"
|
||||
#include "XCAFApp_Application.hxx"
|
||||
#include "XCAFDoc_ShapeTool.hxx"
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <Message_ProgressIndicator.hxx>
|
||||
#include <atomic>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@@ -16,8 +25,25 @@ const int LOAD_STEP_STAGE_UNIT_NUM = 5;
|
||||
typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn;
|
||||
typedef std::function<void(bool isUtf8)> StepIsUtf8Fn;
|
||||
|
||||
struct NamedSolid
|
||||
{
|
||||
NamedSolid(const TopoDS_Shape& s,
|
||||
const std::string& n) : solid{ s }, name{ n } {
|
||||
}
|
||||
const TopoDS_Shape solid;
|
||||
const std::string name;
|
||||
int tri_face_cout = 0;
|
||||
};
|
||||
|
||||
//BBS: Load an step file into a provided model.
|
||||
extern bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
extern bool load_step(const char *path, Model *model,
|
||||
bool& is_cancel,
|
||||
double linear_defletion = 0.003,
|
||||
double angle_defletion = 0.5,
|
||||
bool isSplitCompound = false,
|
||||
ImportStepProgressFn proFn = nullptr,
|
||||
StepIsUtf8Fn isUtf8Fn = nullptr,
|
||||
long& mesh_face_num = *(new long(-1)));
|
||||
|
||||
//BBS: Used to detect what kind of encoded type is used in name field of step
|
||||
// If is encoded in UTF8, the file don't need to be handled, then return the original path directly.
|
||||
@@ -36,14 +62,49 @@ class StepPreProcessor {
|
||||
public:
|
||||
bool preprocess(const char* path, std::string &output_path);
|
||||
static bool isUtf8File(const char* path);
|
||||
private:
|
||||
static bool isUtf8(const std::string str);
|
||||
private:
|
||||
static bool isGBK(const std::string str);
|
||||
static int preNum(const unsigned char byte);
|
||||
//BBS: default is UTF8 for most step file.
|
||||
EncodedType m_encode_type = EncodedType::UTF8;
|
||||
};
|
||||
|
||||
class StepProgressIncdicator : public Message_ProgressIndicator
|
||||
{
|
||||
public:
|
||||
StepProgressIncdicator(std::atomic<bool>& stop_flag) : should_stop(stop_flag){}
|
||||
|
||||
Standard_Boolean UserBreak() override { return should_stop.load(); }
|
||||
|
||||
void Show(const Message_ProgressScope&, const Standard_Boolean) override {
|
||||
std::cout << "Progress: " << GetPosition() << "%" << std::endl;
|
||||
}
|
||||
private:
|
||||
std::atomic<bool>& should_stop;
|
||||
};
|
||||
|
||||
class Step
|
||||
{
|
||||
public:
|
||||
Step(fs::path path, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
Step(std::string path, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
bool load();
|
||||
unsigned int get_triangle_num(double linear_defletion, double angle_defletion);
|
||||
unsigned int get_triangle_num_tbb(double linear_defletion, double angle_defletion);
|
||||
void clean_mesh_data();
|
||||
|
||||
std::atomic<bool> m_stop_mesh;
|
||||
private:
|
||||
std::string m_path;
|
||||
ImportStepProgressFn m_stepFn;
|
||||
StepIsUtf8Fn m_utf8Fn;
|
||||
Handle(XCAFApp_Application) m_app = XCAFApp_Application::GetApplication();
|
||||
Handle(TDocStd_Document) m_doc;
|
||||
Handle(XCAFDoc_ShapeTool) m_shape_tool;
|
||||
std::vector<NamedSolid> m_name_solids;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_STEP_hpp_ */
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "../libslic3r.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cfloat>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "SVG.hpp"
|
||||
#include <Eigen/Dense>
|
||||
#include <functional>
|
||||
#include "GCodeWriter.hpp"
|
||||
|
||||
// BBS: for segment
|
||||
@@ -176,17 +177,68 @@ Model::~Model()
|
||||
Slic3r::remove_backup(*this, true);
|
||||
}
|
||||
|
||||
Model Model::read_from_step(const std::string& input_file,
|
||||
LoadStrategy options,
|
||||
ImportStepProgressFn stepFn,
|
||||
StepIsUtf8Fn stepIsUtf8Fn,
|
||||
std::function<int(Slic3r::Step&, double&, double&, bool&)> step_mesh_fn,
|
||||
double linear_defletion,
|
||||
double angle_defletion,
|
||||
bool is_split_compound)
|
||||
{
|
||||
Model model;
|
||||
bool result = false;
|
||||
bool is_cb_cancel = false;
|
||||
std::string message;
|
||||
Step step_file(input_file);
|
||||
step_file.load();
|
||||
if (step_mesh_fn) {
|
||||
if (step_mesh_fn(step_file, linear_defletion, angle_defletion, is_split_compound) == -1) {
|
||||
Model empty_model;
|
||||
return empty_model;
|
||||
}
|
||||
}
|
||||
result = load_step(input_file.c_str(), &model, is_cb_cancel, linear_defletion, angle_defletion, is_split_compound, stepFn, stepIsUtf8Fn);
|
||||
if (is_cb_cancel) {
|
||||
Model empty_model;
|
||||
return empty_model;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
if (message.empty())
|
||||
throw Slic3r::RuntimeError(_L("Loading of a model file failed."));
|
||||
else
|
||||
throw Slic3r::RuntimeError(message);
|
||||
}
|
||||
|
||||
if (model.objects.empty())
|
||||
throw Slic3r::RuntimeError(_L("The supplied file couldn't be read because it's empty"));
|
||||
|
||||
for (ModelObject *o : model.objects)
|
||||
o->input_file = input_file;
|
||||
|
||||
if (options & LoadStrategy::AddDefaultInstances)
|
||||
model.add_default_instances();
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
// BBS: add part plate related logic
|
||||
// BBS: backup & restore
|
||||
// Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well.
|
||||
Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions,
|
||||
LoadStrategy options, PlateDataPtrs* plate_data, std::vector<Preset*>* project_presets, bool *is_xxx, Semver* file_version, Import3mfProgressFn proFn,
|
||||
ImportstlProgressFn stlFn,
|
||||
ImportStepProgressFn stepFn,
|
||||
StepIsUtf8Fn stepIsUtf8Fn,
|
||||
BBLProject * project,
|
||||
int plate_id,
|
||||
ObjImportColorFn objFn)
|
||||
Model Model::read_from_file(const std::string& input_file,
|
||||
DynamicPrintConfig* config,
|
||||
ConfigSubstitutionContext* config_substitutions,
|
||||
LoadStrategy options,
|
||||
PlateDataPtrs* plate_data,
|
||||
std::vector<Preset*>* project_presets,
|
||||
bool *is_xxx,
|
||||
Semver* file_version,
|
||||
Import3mfProgressFn proFn,
|
||||
ImportstlProgressFn stlFn,
|
||||
BBLProject * project,
|
||||
int plate_id,
|
||||
ObjImportColorFn objFn)
|
||||
{
|
||||
Model model;
|
||||
|
||||
@@ -210,10 +262,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
||||
bool result = false;
|
||||
bool is_cb_cancel = false;
|
||||
std::string message;
|
||||
if (boost::algorithm::iends_with(input_file, ".stp") ||
|
||||
boost::algorithm::iends_with(input_file, ".step"))
|
||||
result = load_step(input_file.c_str(), &model, is_cb_cancel, stepFn, stepIsUtf8Fn);
|
||||
else if (boost::algorithm::iends_with(input_file, ".stl"))
|
||||
if (boost::algorithm::iends_with(input_file, ".stl"))
|
||||
result = load_stl(input_file.c_str(), &model, nullptr, stlFn);
|
||||
else if (boost::algorithm::iends_with(input_file, ".oltp"))
|
||||
result = load_stl(input_file.c_str(), &model, nullptr, stlFn,256);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Geometry.hpp"
|
||||
#include "ObjectID.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "SLA/SupportPoint.hpp"
|
||||
@@ -1540,6 +1541,15 @@ public:
|
||||
|
||||
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model)
|
||||
|
||||
static Model read_from_step(const std::string& input_file,
|
||||
LoadStrategy options,
|
||||
ImportStepProgressFn stepFn,
|
||||
StepIsUtf8Fn stepIsUtf8Fn,
|
||||
std::function<int(Slic3r::Step&, double&, double&, bool&)> step_mesh_fn,
|
||||
double linear_defletion,
|
||||
double angle_defletion,
|
||||
bool is_split_compound);
|
||||
|
||||
//BBS: add part plate related logic
|
||||
// BBS: backup
|
||||
//BBS: is_xxx is used for is_bbs_3mf when loading 3mf, is used for is_inches when loading amf
|
||||
@@ -1549,8 +1559,6 @@ public:
|
||||
LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr,
|
||||
std::vector<Preset*>* project_presets = nullptr, bool* is_xxx = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr,
|
||||
ImportstlProgressFn stlFn = nullptr,
|
||||
ImportStepProgressFn stepFn = nullptr,
|
||||
StepIsUtf8Fn stepIsUtf8Fn = nullptr,
|
||||
BBLProject * project = nullptr,
|
||||
int plate_id = 0,
|
||||
ObjImportColorFn objFn = nullptr
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/nowide/filesystem.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
// boost/property_tree/json_parser/detail/parser.hpp includes boost/bind.hpp, which is deprecated.
|
||||
// Suppress the following boost message:
|
||||
|
||||
@@ -312,6 +312,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/RemovableDriveManager.hpp
|
||||
GUI/SendSystemInfoDialog.cpp
|
||||
GUI/SendSystemInfoDialog.hpp
|
||||
GUI/StepMeshDialog.cpp
|
||||
GUI/StepMeshDialog.hpp
|
||||
GUI/SurfaceDrag.cpp
|
||||
GUI/SurfaceDrag.hpp
|
||||
GUI/TextLines.cpp
|
||||
|
||||
@@ -38,7 +38,7 @@ Points CameraUtils::project(const Camera & camera,
|
||||
return result;
|
||||
}
|
||||
|
||||
Point CameraUtils::project(const Camera &camera, const Vec3d &point)
|
||||
Slic3r::Point CameraUtils::project(const Camera &camera, const Vec3d &point)
|
||||
{
|
||||
// IMPROVE: do it faster when you need it (inspire in project multi point)
|
||||
return project(camera, std::vector{point}).front();
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
/// <returns>projected points by camera into coordinate of camera.
|
||||
/// x(from left to right), y(from top to bottom)</returns>
|
||||
static Points project(const Camera& camera, const std::vector<Vec3d> &points);
|
||||
static Point project(const Camera& camera, const Vec3d &point);
|
||||
static Slic3r::Point project(const Camera& camera, const Vec3d &point);
|
||||
|
||||
/// <summary>
|
||||
/// Create hull around GLVolume in 2d space of camera
|
||||
|
||||
@@ -174,6 +174,8 @@ protected:
|
||||
void find_single();
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_THREAD_DONE, wxCommandEvent);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
#include "DailyTips.hpp"
|
||||
#include "CreatePresetsDialog.hpp"
|
||||
#include "FileArchiveDialog.hpp"
|
||||
#include "StepMeshDialog.hpp"
|
||||
|
||||
using boost::optional;
|
||||
namespace fs = boost::filesystem;
|
||||
@@ -4155,32 +4156,25 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
filament_ids.clear();
|
||||
}
|
||||
};
|
||||
model = Slic3r::Model::read_from_file(
|
||||
path.string(), nullptr, nullptr, strategy, &plate_data, &project_presets, &is_xxx, &file_version, nullptr,
|
||||
[this, &dlg, real_filename, &progress_percent, &file_percent, INPUT_FILES_RATIO, total_files, i, &designer_model_id, &designer_country_code](int current, int total, bool &cancel, std::string &mode_id, std::string &code)
|
||||
{
|
||||
designer_model_id = mode_id;
|
||||
designer_country_code = code;
|
||||
|
||||
bool cont = true;
|
||||
float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * 100.0f * ((float)current / (float)total) / (float)total_files;
|
||||
BOOST_LOG_TRIVIAL(trace) << "load_stl_file: percent(float)=" << percent_float << ", curr = " << current << ", total = " << total;
|
||||
progress_percent = (int)percent_float;
|
||||
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
|
||||
cont = dlg.Update(progress_percent, msg);
|
||||
cancel = !cont;
|
||||
},
|
||||
[this, &dlg, real_filename, &progress_percent, &file_percent, step_percent, INPUT_FILES_RATIO, total_files, i](int load_stage, int current, int total, bool &cancel)
|
||||
{
|
||||
bool cont = true;
|
||||
float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * ((float)step_percent[load_stage] + (float)current * (float)(step_percent[load_stage + 1] - step_percent[load_stage]) / (float)total) / (float)total_files;
|
||||
BOOST_LOG_TRIVIAL(trace) << "load_step_file: percent(float)=" << percent_float << ", stage = " << load_stage << ", curr = " << current << ", total = " << total;
|
||||
progress_percent = (int)percent_float;
|
||||
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
|
||||
cont = dlg.Update(progress_percent, msg);
|
||||
cancel = !cont;
|
||||
},
|
||||
[](int isUtf8StepFile) {
|
||||
if (boost::iends_with(path.string(), ".stp") ||
|
||||
boost::iends_with(path.string(), ".step")) {
|
||||
double linear = string_to_double_decimal_point(wxGetApp().app_config->get("linear_defletion"));
|
||||
if (linear <= 0) linear = 0.003;
|
||||
double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion"));
|
||||
if (angle <= 0) angle = 0.5;
|
||||
bool split_compound = wxGetApp().app_config->get_bool("is_split_compound");
|
||||
model = Slic3r::Model:: read_from_step(path.string(), strategy,
|
||||
[this, &dlg, real_filename, &progress_percent, &file_percent, step_percent, INPUT_FILES_RATIO, total_files, i](int load_stage, int current, int total, bool &cancel)
|
||||
{
|
||||
bool cont = true;
|
||||
float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * ((float)step_percent[load_stage] + (float)current * (float)(step_percent[load_stage + 1] - step_percent[load_stage]) / (float)total) / (float)total_files;
|
||||
BOOST_LOG_TRIVIAL(trace) << "load_step_file: percent(float)=" << percent_float << ", stage = " << load_stage << ", curr = " << current << ", total = " << total;
|
||||
progress_percent = (int)percent_float;
|
||||
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
|
||||
cont = dlg.Update(progress_percent, msg);
|
||||
cancel = !cont;
|
||||
},
|
||||
[](int isUtf8StepFile) {
|
||||
if (!isUtf8StepFile) {
|
||||
const auto no_warn = wxGetApp().app_config->get_bool("step_not_utf8_no_warn");
|
||||
if (!no_warn) {
|
||||
@@ -4194,8 +4188,42 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
}
|
||||
}
|
||||
},
|
||||
nullptr, 0, obj_color_fun);
|
||||
[this, &path, &is_user_cancel, &linear, &angle, &split_compound](Slic3r::Step& file, double& linear_value, double& angle_value, bool& is_split)-> int {
|
||||
if (wxGetApp().app_config->get_bool("enable_step_mesh_setting")) {
|
||||
StepMeshDialog mesh_dlg(nullptr, file, linear, angle);
|
||||
if (mesh_dlg.ShowModal() == wxID_OK) {
|
||||
linear_value = mesh_dlg.get_linear_defletion();
|
||||
angle_value = mesh_dlg.get_angle_defletion();
|
||||
is_split = mesh_dlg.get_split_compound_value();
|
||||
return 1;
|
||||
}
|
||||
}else {
|
||||
linear_value = linear;
|
||||
angle_value = angle;
|
||||
is_split = split_compound;
|
||||
return 1;
|
||||
}
|
||||
is_user_cancel = true;
|
||||
return -1;
|
||||
}, linear, angle, split_compound);
|
||||
}else {
|
||||
model = Slic3r::Model:: read_from_file(
|
||||
path.string(), nullptr, nullptr, strategy, &plate_data, &project_presets, &is_xxx, &file_version, nullptr,
|
||||
[this, &dlg, real_filename, &progress_percent, &file_percent, INPUT_FILES_RATIO, total_files, i, &designer_model_id, &designer_country_code](int current, int total, bool &cancel, std::string &mode_id, std::string &code)
|
||||
{
|
||||
designer_model_id = mode_id;
|
||||
designer_country_code = code;
|
||||
|
||||
bool cont = true;
|
||||
float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * 100.0f * ((float)current / (float)total) / (float)total_files;
|
||||
BOOST_LOG_TRIVIAL(trace) << "load_stl_file: percent(float)=" << percent_float << ", curr = " << current << ", total = " << total;
|
||||
progress_percent = (int)percent_float;
|
||||
wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename));
|
||||
cont = dlg.Update(progress_percent, msg);
|
||||
cancel = !cont;
|
||||
},
|
||||
nullptr, 0, obj_color_fun);
|
||||
}
|
||||
|
||||
if (designer_model_id.empty() && boost::algorithm::iends_with(path.string(), ".stl")) {
|
||||
read_binary_stl(path.string(), designer_model_id, designer_country_code);
|
||||
@@ -5937,8 +5965,17 @@ void Plater::priv::reload_from_disk()
|
||||
std::vector<Preset*> project_presets;
|
||||
|
||||
// BBS: backup
|
||||
new_model = Model::read_from_file(path, nullptr, nullptr, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, &plate_data, &project_presets, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, 0, obj_color_fun);
|
||||
if (boost::iends_with(path, ".stp") ||
|
||||
boost::iends_with(path, ".step")) {
|
||||
double linear = string_to_double_decimal_point(wxGetApp().app_config->get("linear_defletion"));
|
||||
double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion"));
|
||||
bool is_split = wxGetApp().app_config->get_bool("is_split_compound");
|
||||
new_model = Model::read_from_step(path, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, nullptr, nullptr, nullptr, linear, angle, is_split);
|
||||
}else {
|
||||
new_model = Model::read_from_file(path, nullptr, nullptr, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, &plate_data, &project_presets, nullptr, nullptr, nullptr, nullptr, nullptr, 0, obj_color_fun);
|
||||
}
|
||||
|
||||
|
||||
for (ModelObject* model_object : new_model.objects)
|
||||
{
|
||||
model_object->center_around_origin();
|
||||
|
||||
@@ -1225,6 +1225,7 @@ wxWindow* PreferencesDialog::create_general_page()
|
||||
auto item_calc_mode = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time the color changed."), page, _L("If enabled, auto-calculate every time the color changed."), 50, "auto_calculate");
|
||||
auto item_calc_in_long_retract = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time when the filament is changed."), page, _L("If enabled, auto-calculate every time when filament is changed"), 50, "auto_calculate_when_filament_change");
|
||||
auto item_remember_printer_config = create_item_checkbox(_L("Remember printer configuration"), page, _L("If enabled, Orca will remember and switch filament/process configuration for each printer automatically."), 50, "remember_printer_config");
|
||||
auto item_step_mesh_setting = create_item_checkbox(_L("Show the step mesh parameter setting dialog."), page, _L("If enabled,a parameter settings dialog will appear during STEP file import."), 50, "enable_step_mesh_setting");
|
||||
auto item_multi_machine = create_item_checkbox(_L("Multi-device Management(Take effect after restarting Orca)."), page, _L("With this option enabled, you can send a task to multiple devices at the same time and manage multiple devices."), 50, "enable_multi_machine");
|
||||
auto item_auto_arrange = create_item_checkbox(_L("Auto arrange plate after cloning"), page, _L("Auto arrange plate after object cloning"), 50, "auto_arrange");
|
||||
auto title_presets = create_item_title(_L("Presets"), page, _L("Presets"));
|
||||
@@ -1305,6 +1306,7 @@ wxWindow* PreferencesDialog::create_general_page()
|
||||
sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(item_calc_in_long_retract, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(item_multi_machine, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(item_step_mesh_setting, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(item_auto_arrange, 0, wxTOP, FromDIP(3));
|
||||
sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20));
|
||||
sizer_page->Add(item_calc_mode, 0, wxTOP, FromDIP(3));
|
||||
|
||||
386
src/slic3r/GUI/StepMeshDialog.cpp
Normal file
386
src/slic3r/GUI/StepMeshDialog.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
#include "StepMeshDialog.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <wx/event.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/slider.h>
|
||||
#include <wx/dcmemory.h>
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/TextInput.hpp"
|
||||
#include <chrono>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::GUI;
|
||||
|
||||
static int _scale(const int val) { return val * Slic3r::GUI::wxGetApp().em_unit() / 10; }
|
||||
static int _ITEM_WIDTH() { return _scale(30); }
|
||||
#define MIN_DIALOG_WIDTH FromDIP(400)
|
||||
#define SLIDER_WIDTH FromDIP(200)
|
||||
#define SLIDER_HEIGHT FromDIP(25)
|
||||
#define TEXT_CTRL_WIDTH FromDIP(70)
|
||||
#define BUTTON_SIZE wxSize(FromDIP(58), FromDIP(24))
|
||||
#define BUTTON_BORDER FromDIP(int(400 - 58 * 2) / 8)
|
||||
#define SLIDER_SCALE(val) ((val) / 0.001)
|
||||
#define SLIDER_UNSCALE(val) ((val) * 0.001)
|
||||
#define SLIDER_SCALE_10(val) ((val) / 0.01)
|
||||
#define SLIDER_UNSCALE_10(val) ((val) * 0.01)
|
||||
#define LEFT_RIGHT_PADING FromDIP(20)
|
||||
#define FONT_COLOR wxColour("#6B6B6B")
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_THREAD_DONE, wxCommandEvent);
|
||||
|
||||
class CenteredStaticText : public wxStaticText
|
||||
{
|
||||
public:
|
||||
CenteredStaticText(wxWindow* parent, wxWindowID id, const wxString& label, const wxPoint& position, const wxSize& size = wxDefaultSize, long style = 0)
|
||||
: wxStaticText(parent, id, label, position, size, style) {
|
||||
CenterOnPosition(position);
|
||||
}
|
||||
|
||||
void CenterOnPosition(const wxPoint& position) {
|
||||
int textWidth, textHeight;
|
||||
GetTextExtent(GetLabel(), &textWidth, &textHeight);
|
||||
int x = position.x - textWidth / 2;
|
||||
int y = position.y - textHeight / 2;
|
||||
SetPosition(wxPoint(x, y));
|
||||
}
|
||||
};
|
||||
|
||||
void StepMeshDialog::on_dpi_changed(const wxRect& suggested_rect) {
|
||||
};
|
||||
|
||||
bool StepMeshDialog:: validate_number_range(const wxString& value, double min, double max) {
|
||||
double num = 0.0;
|
||||
if (value.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (!value.ToDouble(&num)) {
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (num >= min && num <= max);
|
||||
}
|
||||
|
||||
StepMeshDialog::StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double linear_init, double angle_init)
|
||||
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe),
|
||||
wxID_ANY,
|
||||
_(L("Step file import parameters")),
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxDEFAULT_DIALOG_STYLE /* | wxRESIZE_BORDER*/), m_file(file)
|
||||
{
|
||||
m_linear_last = wxString::Format("%.3f", linear_init);
|
||||
m_angle_last = wxString::Format("%.2f", angle_init);
|
||||
|
||||
Bind(wxEVT_THREAD_DONE, &StepMeshDialog::on_task_done, this);
|
||||
|
||||
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico")
|
||||
% Slic3r::resources_dir()).str();
|
||||
SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
|
||||
bSizer->SetMinSize(wxSize(MIN_DIALOG_WIDTH, -1));
|
||||
|
||||
auto image_bitmap = create_scaled_bitmap("step_mesh_info", this, FromDIP(120));
|
||||
|
||||
// wxPanel* overlay_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, image_bitmap.GetSize(), wxTAB_TRAVERSAL);
|
||||
// overlay_panel->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
// wxStaticBitmap *image = new wxStaticBitmap(overlay_panel, wxID_ANY, image_bitmap, wxDefaultPosition, overlay_panel->GetSize(), 0);
|
||||
|
||||
// CenteredStaticText* text_1 = new CenteredStaticText (overlay_panel, wxID_ANY, _L("Smooth"),
|
||||
// wxPoint(overlay_panel->GetSize().GetWidth() / 6,
|
||||
// overlay_panel->GetSize().GetHeight() / 2));
|
||||
// CenteredStaticText* text_2 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Rough"),
|
||||
// wxPoint(overlay_panel->GetSize().GetWidth() * 5 / 6,
|
||||
// overlay_panel->GetSize().GetHeight() / 2));
|
||||
// CenteredStaticText* text_3 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Reduce Linear"),
|
||||
// wxPoint(overlay_panel->GetSize().GetWidth() / 2,
|
||||
// overlay_panel->GetSize().GetHeight() * 1.3 / 3));
|
||||
// CenteredStaticText* text_4 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Reduce Angle"),
|
||||
// wxPoint(overlay_panel->GetSize().GetWidth() / 2,
|
||||
// overlay_panel->GetSize().GetHeight() * 2 / 3));
|
||||
// CenteredStaticText* text_5 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("More faces"),
|
||||
// wxPoint(overlay_panel->GetSize().GetWidth() / 6,
|
||||
// overlay_panel->GetSize().GetHeight() * 2.8 / 3));
|
||||
// CenteredStaticText* text_6 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Fewer faces"),
|
||||
// wxPoint(overlay_panel->GetSize().GetWidth() * 5 / 6,
|
||||
// overlay_panel->GetSize().GetHeight() * 2.8 / 3));
|
||||
|
||||
// bSizer->Add(overlay_panel, 0, wxALIGN_CENTER | wxALL, 10);
|
||||
|
||||
wxBoxSizer* tips_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
wxStaticText* info = new wxStaticText(this, wxID_ANY, _L("Smaller linear and angular deflections result in higher-quality transformations but increase the processing time."));
|
||||
info->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
|
||||
wxStaticText *tips = new wxStaticText(this, wxID_ANY, _L("View Wiki for more information"));
|
||||
wxFont font(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false);
|
||||
font.SetUnderlined(true);
|
||||
tips->SetForegroundColour(StateColor::darkModeColorFor(wxColour(0, 151, 137)));
|
||||
tips->SetFont(font);
|
||||
tips->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) {
|
||||
wxLaunchDefaultBrowser("https://wiki.bambulab.com/en/software/bambu-studio/step");
|
||||
});
|
||||
info->Wrap(FromDIP(400));
|
||||
tips_sizer->Add(info, 0, wxALIGN_LEFT);
|
||||
tips_sizer->Add(tips, 0, wxALIGN_LEFT);
|
||||
bSizer->Add(tips_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, LEFT_RIGHT_PADING);
|
||||
|
||||
wxBoxSizer* linear_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
//linear_sizer->SetMinSize(wxSize(MIN_DIALOG_WIDTH, -1));
|
||||
wxStaticText* linear_title = new wxStaticText(this,
|
||||
wxID_ANY, _L("Linear Deflection") + ": ");
|
||||
linear_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
|
||||
linear_sizer->Add(linear_title, 0, wxALIGN_LEFT);
|
||||
linear_sizer->AddStretchSpacer(1);
|
||||
wxSlider* linear_slider = new wxSlider(this, wxID_ANY,
|
||||
SLIDER_SCALE(get_linear_defletion()),
|
||||
1, 100, wxDefaultPosition,
|
||||
wxSize(SLIDER_WIDTH, SLIDER_HEIGHT),
|
||||
wxSL_HORIZONTAL);
|
||||
linear_sizer->Add(linear_slider, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5));
|
||||
|
||||
auto linear_input = new ::TextInput(this, m_linear_last, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(TEXT_CTRL_WIDTH, -1), wxTE_CENTER);
|
||||
linear_input->GetTextCtrl()->SetFont(Label::Body_12);
|
||||
linear_input->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
linear_sizer->Add(linear_input, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5));
|
||||
linear_input->Bind(wxEVT_KILL_FOCUS, ([this, linear_input](wxFocusEvent& e) {
|
||||
wxString value = linear_input->GetTextCtrl()->GetValue();
|
||||
if (validate_number_range(value, 0.001, 0.1)) {
|
||||
m_linear_last = value;
|
||||
update_mesh_number_text();
|
||||
} else {
|
||||
MessageDialog msg_dlg(nullptr, _L("Please input a valid value (0.001 < linear deflection < 0.1)"), wxEmptyString, wxICON_WARNING | wxOK);
|
||||
msg_dlg.ShowModal();
|
||||
linear_input->GetTextCtrl()->SetValue(m_linear_last);
|
||||
}
|
||||
e.Skip();
|
||||
}));
|
||||
// textctrl bind slider
|
||||
linear_input->Bind(wxEVT_TEXT, ([this, linear_slider, linear_input](wxCommandEvent& e) {
|
||||
double slider_value_long;
|
||||
int slider_value;
|
||||
wxString value = linear_input->GetTextCtrl()->GetValue();
|
||||
if (value.ToDouble(&slider_value_long)) {
|
||||
slider_value = SLIDER_SCALE(slider_value_long);
|
||||
if (slider_value >= linear_slider->GetMin() && slider_value <= linear_slider->GetMax()) {
|
||||
linear_slider->SetValue(slider_value);
|
||||
}
|
||||
}
|
||||
}));
|
||||
linear_slider->Bind(wxEVT_SLIDER, ([this, linear_slider, linear_input](wxCommandEvent& e) {
|
||||
double slider_value = SLIDER_UNSCALE(linear_slider->GetValue());
|
||||
linear_input->GetTextCtrl()->SetValue(wxString::Format("%.3f", slider_value));
|
||||
m_linear_last = wxString::Format("%.3f", slider_value);
|
||||
}));
|
||||
linear_slider->Bind(wxEVT_LEFT_UP, ([this](wxMouseEvent& e) {
|
||||
update_mesh_number_text();
|
||||
e.Skip();
|
||||
}));
|
||||
|
||||
bSizer->Add(linear_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, LEFT_RIGHT_PADING);
|
||||
|
||||
wxBoxSizer* angle_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxStaticText* angle_title = new wxStaticText(this,
|
||||
wxID_ANY, _L("Angle Deflection") + ": ");
|
||||
angle_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
|
||||
angle_sizer->Add(angle_title, 0, wxALIGN_LEFT);
|
||||
angle_sizer->AddStretchSpacer(1);
|
||||
wxSlider* angle_slider = new wxSlider(this, wxID_ANY,
|
||||
SLIDER_SCALE_10(get_angle_defletion()),
|
||||
1, 100, wxDefaultPosition,
|
||||
wxSize(SLIDER_WIDTH, SLIDER_HEIGHT),
|
||||
wxSL_HORIZONTAL);
|
||||
angle_sizer->Add(angle_slider, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5));
|
||||
|
||||
auto angle_input = new ::TextInput(this, m_angle_last, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(TEXT_CTRL_WIDTH, -1), wxTE_CENTER);
|
||||
angle_input->GetTextCtrl()->SetFont(Label::Body_12);
|
||||
angle_input->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
angle_sizer->Add(angle_input, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5));
|
||||
angle_input->Bind(wxEVT_KILL_FOCUS, ([this, angle_input](wxFocusEvent& e) {
|
||||
wxString value = angle_input->GetTextCtrl()->GetValue();
|
||||
if (validate_number_range(value, 0.01, 1)) {
|
||||
m_angle_last = value;
|
||||
update_mesh_number_text();
|
||||
} else {
|
||||
MessageDialog msg_dlg(nullptr, _L("Please input a valid value (0.01 < angle deflection < 1.0)"), wxEmptyString, wxICON_WARNING | wxOK);
|
||||
msg_dlg.ShowModal();
|
||||
angle_input->GetTextCtrl()->SetValue(m_angle_last);
|
||||
}
|
||||
e.Skip();
|
||||
}));
|
||||
// textctrl bind slider
|
||||
angle_input->Bind(wxEVT_TEXT, ([this, angle_slider, angle_input](wxCommandEvent& e) {
|
||||
double slider_value_long;
|
||||
int slider_value;
|
||||
wxString value = angle_input->GetTextCtrl()->GetValue();
|
||||
if (value.ToDouble(&slider_value_long)) {
|
||||
slider_value = SLIDER_SCALE_10(slider_value_long);
|
||||
if (slider_value >= angle_slider->GetMin() && slider_value <= angle_slider->GetMax()) {
|
||||
angle_slider->SetValue(slider_value);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
angle_slider->Bind(wxEVT_SLIDER, ([this, angle_slider, angle_input](wxCommandEvent& e) {
|
||||
double slider_value = SLIDER_UNSCALE_10(angle_slider->GetValue());
|
||||
angle_input->GetTextCtrl()->SetValue(wxString::Format("%.2f", slider_value));
|
||||
m_angle_last = wxString::Format("%.2f", slider_value);
|
||||
}));
|
||||
angle_slider->Bind(wxEVT_LEFT_UP, ([this](wxMouseEvent& e) {
|
||||
update_mesh_number_text();
|
||||
e.Skip();
|
||||
}));
|
||||
|
||||
bSizer->Add(angle_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING);
|
||||
|
||||
wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_split_compound_checkbox = new wxCheckBox(this, wxID_ANY, _L("Split compound and compsolid into multiple objects"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_split_compound_checkbox->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
|
||||
m_split_compound_checkbox->SetValue(wxGetApp().app_config->get_bool("is_split_compound"));
|
||||
check_sizer->Add(m_split_compound_checkbox, 0, wxALIGN_LEFT);
|
||||
bSizer->Add(check_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING);
|
||||
|
||||
wxBoxSizer* mesh_face_number_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxStaticText *mesh_face_number_title = new wxStaticText(this, wxID_ANY, _L("Number of triangular facets") + ": ");
|
||||
mesh_face_number_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
|
||||
mesh_face_number_text = new wxStaticText(this, wxID_ANY, _L("0"));
|
||||
mesh_face_number_text->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
|
||||
mesh_face_number_text->SetMinSize(wxSize(FromDIP(150), -1));
|
||||
mesh_face_number_sizer->Add(mesh_face_number_title, 0, wxALIGN_LEFT);
|
||||
mesh_face_number_sizer->Add(mesh_face_number_text, 0, wxALIGN_LEFT);
|
||||
bSizer->Add(mesh_face_number_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING);
|
||||
|
||||
wxBoxSizer* bSizer_button = new wxBoxSizer(wxHORIZONTAL);
|
||||
bSizer_button->SetMinSize(wxSize(FromDIP(100), -1));
|
||||
m_checkbox = new wxCheckBox(this, wxID_ANY, _L("Don't show again"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_checkbox->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR));
|
||||
bSizer_button->Add(m_checkbox, 0, wxALIGN_LEFT);
|
||||
bSizer_button->AddStretchSpacer(1);
|
||||
StateColor btn_bg_green(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(AMS_CONTROL_BRAND_COLOUR, StateColor::Normal));
|
||||
m_button_ok = new Button(this, _L("OK"));
|
||||
m_button_ok->SetBackgroundColor(btn_bg_green);
|
||||
m_button_ok->SetBorderColor(*wxWHITE);
|
||||
m_button_ok->SetTextColor(wxColour(0xFFFFFE));
|
||||
m_button_ok->SetFont(Label::Body_12);
|
||||
m_button_ok->SetSize(BUTTON_SIZE);
|
||||
m_button_ok->SetMinSize(BUTTON_SIZE);
|
||||
m_button_ok->SetCornerRadius(FromDIP(12));
|
||||
bSizer_button->Add(m_button_ok, 0, wxALIGN_RIGHT, BUTTON_BORDER);
|
||||
|
||||
m_button_ok->Bind(wxEVT_LEFT_DOWN, [this, angle_input, linear_input](wxMouseEvent& e) {
|
||||
stop_task();
|
||||
if (validate_number_range(angle_input->GetTextCtrl()->GetValue(), 0.01, 1) &&
|
||||
validate_number_range(linear_input->GetTextCtrl()->GetValue(), 0.001, 0.1)) {
|
||||
if (m_checkbox->IsChecked()) {
|
||||
wxGetApp().app_config->set_bool("enable_step_mesh_setting", false);
|
||||
}
|
||||
wxGetApp().app_config->set_bool("is_split_compound", m_split_compound_checkbox->GetValue());
|
||||
wxGetApp().app_config->set("linear_defletion", float_to_string_decimal_point(get_linear_defletion(), 3));
|
||||
wxGetApp().app_config->set("angle_defletion", float_to_string_decimal_point(get_angle_defletion(), 2));
|
||||
|
||||
EndModal(wxID_OK);
|
||||
}
|
||||
SetFocusIgnoringChildren();
|
||||
});
|
||||
|
||||
StateColor btn_bg_white(std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed), std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
|
||||
std::pair<wxColour, int>(*wxWHITE, StateColor::Normal));
|
||||
|
||||
m_button_cancel = new Button(this, _L("Cancel"));
|
||||
m_button_cancel->SetBackgroundColor(btn_bg_white);
|
||||
m_button_cancel->SetBorderColor(wxColour(38, 46, 48));
|
||||
m_button_cancel->SetFont(Label::Body_12);
|
||||
m_button_cancel->SetSize(BUTTON_SIZE);
|
||||
m_button_cancel->SetMinSize(BUTTON_SIZE);
|
||||
m_button_cancel->SetCornerRadius(FromDIP(12));
|
||||
bSizer_button->Add(m_button_cancel, 0, wxALIGN_RIGHT | wxLEFT, BUTTON_BORDER);
|
||||
|
||||
m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) {
|
||||
stop_task();
|
||||
EndModal(wxID_CANCEL);
|
||||
});
|
||||
|
||||
bSizer->Add(bSizer_button, 1, wxEXPAND | wxALL, LEFT_RIGHT_PADING);
|
||||
|
||||
this->SetSizer(bSizer);
|
||||
update_mesh_number_text();
|
||||
this->Layout();
|
||||
bSizer->Fit(this);
|
||||
|
||||
this->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {
|
||||
SetFocusIgnoringChildren();
|
||||
});
|
||||
mesh_face_number_text->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {
|
||||
SetFocusIgnoringChildren();
|
||||
});
|
||||
|
||||
this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) {
|
||||
stop_task();
|
||||
EndModal(wxID_CANCEL);
|
||||
});
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
StepMeshDialog::~StepMeshDialog()
|
||||
{
|
||||
stop_task();
|
||||
}
|
||||
|
||||
void StepMeshDialog::on_task_done(wxCommandEvent& event)
|
||||
{
|
||||
wxString text = event.GetString();
|
||||
mesh_face_number_text->SetLabel(text);
|
||||
if(m_task) {
|
||||
if (m_task->joinable()) {
|
||||
m_task->join();
|
||||
delete m_task;
|
||||
m_task = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StepMeshDialog::stop_task()
|
||||
{
|
||||
if(m_task) {
|
||||
m_file.m_stop_mesh.store(true);
|
||||
if (m_task->joinable()) {
|
||||
m_task->join();
|
||||
delete m_task;
|
||||
m_task = nullptr;
|
||||
}
|
||||
m_file.m_stop_mesh.store(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void StepMeshDialog::update_mesh_number_text()
|
||||
{
|
||||
if ((m_last_linear == get_linear_defletion()) && (m_last_angle == get_angle_defletion()) && (m_mesh_number != 0))
|
||||
return;
|
||||
wxString newText = wxString::Format(_L("Calculating, please wait..."));
|
||||
mesh_face_number_text->SetLabel(newText);
|
||||
stop_task();
|
||||
if (!m_task) {
|
||||
m_task = new boost::thread(Slic3r::create_thread([this]() -> void {
|
||||
m_mesh_number = m_file.get_triangle_num(get_linear_defletion(), get_angle_defletion());
|
||||
if (m_mesh_number != 0) {
|
||||
wxString number_text = wxString::Format("%d", m_mesh_number);
|
||||
wxCommandEvent event(wxEVT_THREAD_DONE);
|
||||
event.SetString(number_text);
|
||||
wxPostEvent(this, event);
|
||||
m_last_linear = get_linear_defletion();
|
||||
m_last_angle = get_angle_defletion();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
55
src/slic3r/GUI/StepMeshDialog.hpp
Normal file
55
src/slic3r/GUI/StepMeshDialog.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef _STEP_MESH_DIALOG_H_
|
||||
#define _STEP_MESH_DIALOG_H_
|
||||
|
||||
#include <thread>
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "libslic3r/Format/STEP.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
class Button;
|
||||
|
||||
class StepMeshDialog : public Slic3r::GUI::DPIDialog
|
||||
{
|
||||
public:
|
||||
StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double linear_init, double angle_init);
|
||||
~StepMeshDialog() override;
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
inline double get_linear_defletion() {
|
||||
double value;
|
||||
if (m_linear_last.ToDouble(&value)) {
|
||||
return value;
|
||||
}else {
|
||||
return m_last_linear;
|
||||
}
|
||||
}
|
||||
inline double get_angle_defletion() {
|
||||
double value;
|
||||
if (m_angle_last.ToDouble(&value)) {
|
||||
return value;
|
||||
} else {
|
||||
return m_last_angle;
|
||||
}
|
||||
}
|
||||
inline bool get_split_compound_value() {
|
||||
return m_split_compound_checkbox->GetValue();
|
||||
}
|
||||
private:
|
||||
Slic3r::Step& m_file;
|
||||
Button* m_button_ok = nullptr;
|
||||
Button* m_button_cancel = nullptr;
|
||||
wxCheckBox* m_checkbox = nullptr;
|
||||
wxCheckBox* m_split_compound_checkbox = nullptr;
|
||||
wxString m_linear_last;
|
||||
wxString m_angle_last;
|
||||
wxStaticText* mesh_face_number_text;
|
||||
double m_last_linear = 0.003;
|
||||
double m_last_angle = 0.5;
|
||||
unsigned int m_mesh_number = 0;
|
||||
boost::thread* m_task {nullptr};
|
||||
bool validate_number_range(const wxString& value, double min, double max);
|
||||
void update_mesh_number_text();
|
||||
void on_task_done(wxCommandEvent& event);
|
||||
void stop_task();
|
||||
};
|
||||
|
||||
#endif // _STEP_MESH_DIALOG_H_
|
||||
@@ -303,7 +303,7 @@ static void read_model_from_file(const std::string& input_file, Model& model)
|
||||
std::vector<Preset *> project_presets;
|
||||
|
||||
model = Model::read_from_file(input_file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets,
|
||||
&is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, nullptr, nullptr, plate_to_slice);
|
||||
&is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, plate_to_slice);
|
||||
|
||||
model.add_default_instances();
|
||||
for (auto object : model.objects)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#include <CommCtrl.h>
|
||||
#endif
|
||||
|
||||
#include <float.h>
|
||||
|
||||
Reference in New Issue
Block a user