mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-06 14:37:36 -06:00
Port STEP import dialog from BambuStudio (#9102)
 Thanks BambuLab! Fix #8820
This commit is contained in:
commit
a519ea34a5
19 changed files with 817 additions and 74 deletions
5
resources/images/step_mesh_info.svg
Normal file
5
resources/images/step_mesh_info.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="407" height="141" viewBox="0 0 407 141" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M377.365 14.672L336.592 1.42407L295.819 14.672L270.62 49.3556V92.2268L295.819 126.91L336.592 140.158L377.365 126.91L402.564 92.2268V49.3556L377.365 14.672Z" stroke="#262E30"/>
|
||||
<path d="M97.075 6.68967L70.524 1.4082L43.972 6.68967L21.462 21.7301L6.42197 44.2396L1.14096 70.7913L6.42197 97.343L21.462 119.852L43.972 134.893L70.524 140.174L97.075 134.893L119.585 119.852L134.625 97.343L139.907 70.7913L134.625 44.2395L119.585 21.7301L97.075 6.68967Z" stroke="#262E30"/>
|
||||
<path d="M149.063 80.4961C148.672 80.1056 148.672 79.4724 149.063 79.0819L155.427 72.7179C155.817 72.3274 156.451 72.3274 156.841 72.7179C157.232 73.1084 157.232 73.7416 156.841 74.1321L151.184 79.789L156.841 85.4458C157.232 85.8363 157.232 86.4695 156.841 86.86C156.451 87.2506 155.817 87.2506 155.427 86.86L149.063 80.4961ZM255 80.789H149.77V78.789H255V80.789Z" fill="#262E30"/>
|
||||
</svg>
|
After Width: | Height: | Size: 964 B |
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue