NEW:import vertex and mtl color from obj file

Jira: STUDIO-6805

Change-Id: Iaacb13ee2451effdb83e5aba4b7fe1637b7fc95f
This commit is contained in:
zhou.xu 2024-04-17 17:33:19 +08:00 committed by Lane.Wei
parent db98556fe8
commit e3407b3c17
19 changed files with 1830 additions and 77 deletions

View File

@ -791,7 +791,8 @@ static int construct_assemble_list(std::vector<assemble_plate_info_t> &assemble_
else if (boost::algorithm::iends_with(assemble_object.path, ".obj")) else if (boost::algorithm::iends_with(assemble_object.path, ".obj"))
{ {
std::string message; std::string message;
bool result = load_obj(path_str, &mesh, message); ObjInfo obj_info;
bool result = load_obj(path_str, &mesh, obj_info, message);
if (!result) { if (!result) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": failed to read a valid mesh from obj file %1%, plate index %2%, object index %3%, error %4%") % assemble_object.path % (index + 1) % (obj_index + 1) % message; BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": failed to read a valid mesh from obj file %1%, plate index %2%, object index %3%, error %4%") % assemble_object.path % (index + 1) % (obj_index + 1) % message;
return CLI_DATA_FILE_ERROR; return CLI_DATA_FILE_ERROR;

View File

@ -213,6 +213,7 @@ set(lisbslic3r_sources
Arrange.cpp Arrange.cpp
NormalUtils.cpp NormalUtils.cpp
NormalUtils.hpp NormalUtils.hpp
ObjColorUtils.hpp
Orient.hpp Orient.hpp
Orient.cpp Orient.cpp
MultiPoint.cpp MultiPoint.cpp
@ -432,6 +433,7 @@ set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE)
cmake_policy(PUSH) cmake_policy(PUSH)
cmake_policy(SET CMP0011 NEW) cmake_policy(SET CMP0011 NEW)
find_package(CGAL REQUIRED) find_package(CGAL REQUIRED)
find_package(OpenCV REQUIRED core)
cmake_policy(POP) cmake_policy(POP)
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp
@ -523,6 +525,7 @@ target_link_libraries(libslic3r
${OCCT_LIBS} ${OCCT_LIBS}
Clipper2 Clipper2
mcut mcut
opencv_world
) )
if(NOT WIN32) if(NOT WIN32)

View File

@ -6,7 +6,15 @@
static const float INV_255 = 1.0f / 255.0f; static const float INV_255 = 1.0f / 255.0f;
namespace Slic3r { namespace Slic3r {
bool color_is_equal(const RGBA a, const RGBA& b)
{
for (size_t i = 0; i < 4; i++) {
if (abs(a[i] - b[i]) > 0.01) {
return false;
}
}
return true;
}
// Conversion from RGB to HSV color space // Conversion from RGB to HSV color space
// The input RGB values are in the range [0, 1] // The input RGB values are in the range [0, 1]
// The output HSV values are in the ranges h = [0, 360], and s, v = [0, 1] // The output HSV values are in the ranges h = [0, 360], and s, v = [0, 1]

View File

@ -5,7 +5,10 @@
#include <algorithm> #include <algorithm>
namespace Slic3r { namespace Slic3r {
using RGB = std::array<float, 3>;
using RGBA = std::array<float, 4>;
const RGBA UNDEFINE_COLOR = {0,0,0,0};
bool color_is_equal(const RGBA a, const RGBA &b);
class ColorRGB class ColorRGB
{ {
std::array<float, 3> m_data{1.0f, 1.0f, 1.0f}; std::array<float, 3> m_data{1.0f, 1.0f, 1.0f};
@ -81,7 +84,9 @@ public:
ColorRGBA& operator = (const ColorRGBA& other) { m_data = other.m_data; return *this; } ColorRGBA& operator = (const ColorRGBA& other) { m_data = other.m_data; return *this; }
bool operator == (const ColorRGBA& other) const { return m_data == other.m_data; } bool operator==(const ColorRGBA &other) const{
return color_is_equal(m_data, other.m_data);
}
bool operator != (const ColorRGBA& other) const { return !operator==(other); } bool operator != (const ColorRGBA& other) const { return !operator==(other); }
bool operator < (const ColorRGBA& other) const; bool operator < (const ColorRGBA& other) const;
bool operator > (const ColorRGBA& other) const; bool operator > (const ColorRGBA& other) const;

View File

@ -21,19 +21,39 @@
namespace Slic3r { namespace Slic3r {
bool load_obj(const char *path, TriangleMesh *meshptr, std::string &message) bool load_obj(const char *path, TriangleMesh *meshptr, ObjInfo& obj_info, std::string &message)
{ {
if (meshptr == nullptr) if (meshptr == nullptr)
return false; return false;
// Parse the OBJ file. // Parse the OBJ file.
ObjParser::ObjData data; ObjParser::ObjData data;
ObjParser::MtlData mtl_data;
if (! ObjParser::objparse(path, data)) { if (! ObjParser::objparse(path, data)) {
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path; BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path;
message = _L("load_obj: failed to parse"); message = _L("load_obj: failed to parse");
return false; return false;
} }
bool exist_mtl = false;
if (data.mtllibs.size() > 0) { // read mtl
for (auto mtl_name : data.mtllibs) {
boost::filesystem::path full_path(path);
std::string dir = full_path.parent_path().string();
auto mtl_file = dir + "/" + mtl_name;
boost::filesystem::path mtl_path(mtl_file);
auto _mtl_path = mtl_path.string().c_str();
if (boost::filesystem::exists(mtl_path)) {
if (!ObjParser::mtlparse(_mtl_path, mtl_data)) {
BOOST_LOG_TRIVIAL(error) << "load_obj:load_mtl: failed to parse " << _mtl_path;
message = _L("load mtl in obj: failed to parse");
return false;
}
}
else {
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to load mtl_path:" << _mtl_path;
}
}
exist_mtl = true;
}
// Count the faces and verify, that all faces are triangular. // Count the faces and verify, that all faces are triangular.
size_t num_faces = 0; size_t num_faces = 0;
size_t num_quads = 0; size_t num_quads = 0;
@ -59,17 +79,27 @@ bool load_obj(const char *path, TriangleMesh *meshptr, std::string &message)
i = j; i = j;
} }
} }
// Convert ObjData into indexed triangle set. // Convert ObjData into indexed triangle set.
indexed_triangle_set its; indexed_triangle_set its;
size_t num_vertices = data.coordinates.size() / 4; size_t num_vertices = data.coordinates.size() / OBJ_VERTEX_LENGTH;
its.vertices.reserve(num_vertices); its.vertices.reserve(num_vertices);
its.indices.reserve(num_faces + num_quads); its.indices.reserve(num_faces + num_quads);
for (size_t i = 0; i < num_vertices; ++ i) { if (exist_mtl) {
size_t j = i << 2; obj_info.is_single_mtl = data.usemtls.size() == 1 && mtl_data.new_mtl_unmap.size() == 1;
its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]); obj_info.face_colors.reserve(num_faces + num_quads);
} }
int indices[4]; bool has_color = data.has_vertex_color;
for (size_t i = 0; i < num_vertices; ++ i) {
size_t j = i * OBJ_VERTEX_LENGTH;
its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]);
if (data.has_vertex_color) {
RGBA color{std::clamp(data.coordinates[j + 3], 0.f, 1.f), std::clamp(data.coordinates[j + 4], 0.f, 1.f), std::clamp(data.coordinates[j + 5], 0.f, 1.f),
std::clamp(data.coordinates[j + 6], 0.f, 1.f)};
obj_info.vertex_colors.emplace_back(color);
}
}
int indices[ONE_FACE_SIZE];
int uvs[ONE_FACE_SIZE];
for (size_t i = 0; i < data.vertices.size();) for (size_t i = 0; i < data.vertices.size();)
if (data.vertices[i].coordIdx == -1) if (data.vertices[i].coordIdx == -1)
++ i; ++ i;
@ -79,20 +109,77 @@ bool load_obj(const char *path, TriangleMesh *meshptr, std::string &message)
if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) { if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) {
break; break;
} else { } else {
assert(cnt < 4); assert(cnt < OBJ_VERTEX_LENGTH);
if (vertex.coordIdx < 0 || vertex.coordIdx >= int(its.vertices.size())) { if (vertex.coordIdx < 0 || vertex.coordIdx >= int(its.vertices.size())) {
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index."; BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
message = _L("The file contains invalid vertex index."); message = _L("The file contains invalid vertex index.");
return false; return false;
} }
indices[cnt ++] = vertex.coordIdx; indices[cnt] = vertex.coordIdx;
uvs[cnt] = vertex.textureCoordIdx;
cnt++;
} }
if (cnt) { if (cnt) {
assert(cnt == 3 || cnt == 4); assert(cnt == 3 || cnt == 4);
// Insert one or two faces (triangulate a quad). // Insert one or two faces (triangulate a quad).
its.indices.emplace_back(indices[0], indices[1], indices[2]); its.indices.emplace_back(indices[0], indices[1], indices[2]);
if (cnt == 4) int face_index =its.indices.size() - 1;
RGBA face_color;
auto set_face_color = [&uvs, &data, &mtl_data, &obj_info, &face_color](int face_index, const std::string mtl_name) {
if (mtl_data.new_mtl_unmap.find(mtl_name) != mtl_data.new_mtl_unmap.end()) {
for (size_t n = 0; n < 3; n++) {
if (float(mtl_data.new_mtl_unmap[mtl_name]->Ka[n] + mtl_data.new_mtl_unmap[mtl_name]->Kd[n]) > 1.0) {
face_color[n] = std::clamp(float(mtl_data.new_mtl_unmap[mtl_name]->Kd[n]), 0.f, 1.f);
}
else {
face_color[n] = std::clamp(float(mtl_data.new_mtl_unmap[mtl_name]->Ka[n] + mtl_data.new_mtl_unmap[mtl_name]->Kd[n]), 0.f, 1.f);
}
}
face_color[3] = 1.0; // default alpha
if (mtl_data.new_mtl_unmap[mtl_name]->map_Kd.size() > 0) {
auto png_name = mtl_data.new_mtl_unmap[mtl_name]->map_Kd;
obj_info.has_uv_png = true;
if (obj_info.pngs.find(png_name) == obj_info.pngs.end()) { obj_info.pngs[png_name] = false; }
obj_info.uv_map_pngs[face_index] = png_name;
}
if (data.textureCoordinates.size() > 0) {
Vec2f uv0(data.textureCoordinates[uvs[0] * 2], data.textureCoordinates[uvs[0] * 2 + 1]);
Vec2f uv1(data.textureCoordinates[uvs[1] * 2], data.textureCoordinates[uvs[1] * 2 + 1]);
Vec2f uv2(data.textureCoordinates[uvs[2] * 2], data.textureCoordinates[uvs[2] * 2 + 1]);
std::array<Vec2f, 3> uv_array{uv0, uv1, uv2};
obj_info.uvs.emplace_back(uv_array);
}
obj_info.face_colors.emplace_back(face_color);
}
};
auto set_face_color_by_mtl = [&data, &set_face_color](int face_index) {
if (data.usemtls.size() == 1) {
set_face_color(face_index, data.usemtls[0].name);
} else {
for (size_t k = 0; k < data.usemtls.size(); k++) {
auto mtl = data.usemtls[k];
if (mtl.vertexIdxEnd == -1 && face_index >= (mtl.vertexIdxFirst / ONE_FACE_SIZE)) {
set_face_color(face_index, data.usemtls[k].name);
break;
} else if (mtl.vertexIdxEnd != -1 &&
face_index >= (mtl.vertexIdxFirst / ONE_FACE_SIZE)
&& face_index < (mtl.vertexIdxEnd / ONE_FACE_SIZE)) {
set_face_color(face_index, data.usemtls[k].name);
break;
}
}
}
};
if (exist_mtl) {
set_face_color_by_mtl(face_index);
}
if (cnt == 4) {
its.indices.emplace_back(indices[0], indices[2], indices[3]); its.indices.emplace_back(indices[0], indices[2], indices[3]);
int face_index = its.indices.size() - 1;
if (exist_mtl) {
set_face_color_by_mtl(face_index);
}
}
} }
} }
@ -107,11 +194,11 @@ bool load_obj(const char *path, TriangleMesh *meshptr, std::string &message)
return true; return true;
} }
bool load_obj(const char *path, Model *model, std::string &message, const char *object_name_in) bool load_obj(const char *path, Model *model, ObjInfo& obj_info, std::string &message, const char *object_name_in)
{ {
TriangleMesh mesh; TriangleMesh mesh;
bool ret = load_obj(path, &mesh, message); bool ret = load_obj(path, &mesh, obj_info, message);
if (ret) { if (ret) {
std::string object_name; std::string object_name;
@ -120,7 +207,6 @@ bool load_obj(const char *path, Model *model, std::string &message, const char *
object_name.assign((last_slash == nullptr) ? path : last_slash + 1); object_name.assign((last_slash == nullptr) ? path : last_slash + 1);
} else } else
object_name.assign(object_name_in); object_name.assign(object_name_in);
model->add_object(object_name.c_str(), path, std::move(mesh)); model->add_object(object_name.c_str(), path, std::move(mesh));
} }

View File

@ -1,15 +1,27 @@
#ifndef slic3r_Format_OBJ_hpp_ #ifndef slic3r_Format_OBJ_hpp_
#define slic3r_Format_OBJ_hpp_ #define slic3r_Format_OBJ_hpp_
#include "libslic3r/Color.hpp"
#include <unordered_map>
namespace Slic3r { namespace Slic3r {
class TriangleMesh; class TriangleMesh;
class Model; class Model;
class ModelObject; class ModelObject;
typedef std::function<void(std::vector<RGBA> &input_colors, bool is_single_color, std::vector<unsigned char> &filament_ids, unsigned char &first_extruder_id)> ObjImportColorFn;
// Load an OBJ file into a provided model. // Load an OBJ file into a provided model.
extern bool load_obj(const char *path, TriangleMesh *mesh, std::string &message); struct ObjInfo {
extern bool load_obj(const char *path, Model *model, std::string &message, const char *object_name = nullptr); std::vector<RGBA> vertex_colors;
std::vector<RGBA> face_colors;
bool is_single_mtl{false};
std::vector<std::array<Vec2f,3>> uvs;
std::string obj_dircetory;
std::map<std::string,bool> pngs;
std::unordered_map<int, std::string> uv_map_pngs;
bool has_uv_png{false};
};
extern bool load_obj(const char *path, TriangleMesh *mesh, ObjInfo &vertex_colors, std::string &message);
extern bool load_obj(const char *path, Model *model, ObjInfo &vertex_colors, std::string &message, const char *object_name = nullptr);
extern bool store_obj(const char *path, TriangleMesh *mesh); extern bool store_obj(const char *path, TriangleMesh *mesh);
extern bool store_obj(const char *path, ModelObject *model); extern bool store_obj(const char *path, ModelObject *model);

View File

@ -9,16 +9,12 @@
#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/LocalesUtils.hpp"
namespace ObjParser { namespace ObjParser {
#define EATWS() while (*line == ' ' || *line == '\t') ++line
static bool obj_parseline(const char *line, ObjData &data) static bool obj_parseline(const char *line, ObjData &data)
{ {
#define EATWS() while (*line == ' ' || *line == '\t') ++ line
if (*line == 0) if (*line == 0)
return true; return true;
assert(Slic3r::is_decimal_separator_point()); assert(Slic3r::is_decimal_separator_point());
// Ignore whitespaces at the beginning of the line. // Ignore whitespaces at the beginning of the line.
//FIXME is this a good idea? //FIXME is this a good idea?
EATWS(); EATWS();
@ -55,19 +51,19 @@ static bool obj_parseline(const char *line, ObjData &data)
line = endptr; line = endptr;
EATWS(); EATWS();
} }
double w = 0; /*double w = 0;
if (*line != 0) { if (*line != 0) {
w = strtod(line, &endptr); w = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0)) if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false; return false;
line = endptr; line = endptr;
EATWS(); EATWS();
} }*/
if (*line != 0) if (*line != 0)
return false; return false;
data.textureCoordinates.push_back((float)u); data.textureCoordinates.push_back((float)u);
data.textureCoordinates.push_back((float)v); data.textureCoordinates.push_back((float)v);
data.textureCoordinates.push_back((float)w); //data.textureCoordinates.push_back((float)w);
break; break;
} }
case 'n': case 'n':
@ -156,13 +152,33 @@ static bool obj_parseline(const char *line, ObjData &data)
return false; return false;
line = endptr; line = endptr;
EATWS(); EATWS();
double w = 1.0; double color_x = 0.0, color_y = 0.0, color_z = 0.0, color_w = 0.0;//undefine color
if (*line != 0) { if (*line != 0) {
w = strtod(line, &endptr); if (!data.has_vertex_color) {
data.has_vertex_color = true;
}
color_x = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0)) if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false; return false;
line = endptr; line = endptr;
EATWS(); EATWS();
color_y = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
color_z = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
color_w = 1.0;//default define alpha = 1.0
if (*line != 0) {
color_w = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0)) return false;
line = endptr;
EATWS();
}
} }
// the following check is commented out because there may be obj files containing extra data, as those generated by Meshlab, // the following check is commented out because there may be obj files containing extra data, as those generated by Meshlab,
// see https://dev.prusa3d.com/browse/SPE-1019 for an example, // see https://dev.prusa3d.com/browse/SPE-1019 for an example,
@ -172,7 +188,10 @@ static bool obj_parseline(const char *line, ObjData &data)
data.coordinates.push_back((float)x); data.coordinates.push_back((float)x);
data.coordinates.push_back((float)y); data.coordinates.push_back((float)y);
data.coordinates.push_back((float)z); data.coordinates.push_back((float)z);
data.coordinates.push_back((float)w); data.coordinates.push_back((float) color_x);
data.coordinates.push_back((float) color_y);
data.coordinates.push_back((float) color_z);
data.coordinates.push_back((float) color_w);
break; break;
} }
} }
@ -263,6 +282,9 @@ static bool obj_parseline(const char *line, ObjData &data)
// usemtl [material name] // usemtl [material name]
// printf("usemtl %s\r\n", line); // printf("usemtl %s\r\n", line);
EATWS(); EATWS();
if (data.usemtls.size()>0) {
data.usemtls.back().vertexIdxEnd = (int) data.vertices.size();
}
ObjUseMtl usemtl; ObjUseMtl usemtl;
usemtl.vertexIdxFirst = (int)data.vertices.size(); usemtl.vertexIdxFirst = (int)data.vertices.size();
usemtl.name = line; usemtl.name = line;
@ -323,6 +345,191 @@ static bool obj_parseline(const char *line, ObjData &data)
return true; return true;
} }
static std::string cur_mtl_name = "";
static bool mtl_parseline(const char *line, MtlData &data)
{
if (*line == 0) return true;
assert(Slic3r::is_decimal_separator_point());
// Ignore whitespaces at the beginning of the line.
// FIXME is this a good idea?
EATWS();
char c1 = *line++;
switch (c1) {
case '#': {// Comment, ignore the rest of the line.
break;
}
case 'n': {
if (*(line++) != 'e' || *(line++) != 'w' || *(line++) != 'm' || *(line++) != 't' || *(line++) != 'l')
return false;
EATWS();
ObjNewMtl new_mtl;
cur_mtl_name = line;
data.new_mtl_unmap[cur_mtl_name] = std::make_shared<ObjNewMtl>();
break;
}
case 'm': {
if (*(line++) != 'a' || *(line++) != 'p' || *(line++) != '_' || *(line++) != 'K' || *(line++) != 'd') return false;
EATWS();
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->map_Kd = line;
}
break;
}
case 'N': {
char cur_char = *(line++);
if (cur_char == 's') {
EATWS();
char * endptr = 0;
double ns = strtod(line, &endptr);
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->Ns = (float) ns;
}
} else if (cur_char == 'i') {
EATWS();
char * endptr = 0;
double ni = strtod(line, &endptr);
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->Ni = (float) ni;
}
}
break;
}
case 'K': {
char cur_char = *(line++);
if (cur_char == 'a') {
EATWS();
char * endptr = 0;
double x = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t')) return false;
line = endptr;
EATWS();
double y = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t')) return false;
line = endptr;
EATWS();
double z = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0)) return false;
line = endptr;
EATWS();
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->Ka[0] = x;
data.new_mtl_unmap[cur_mtl_name]->Ka[1] = y;
data.new_mtl_unmap[cur_mtl_name]->Ka[2] = z;
}
} else if (cur_char == 'd') {
EATWS();
char * endptr = 0;
double x = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t')) return false;
line = endptr;
EATWS();
double y = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t')) return false;
line = endptr;
EATWS();
double z = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0)) return false;
line = endptr;
EATWS();
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->Kd[0] = x;
data.new_mtl_unmap[cur_mtl_name]->Kd[1] = y;
data.new_mtl_unmap[cur_mtl_name]->Kd[2] = z;
}
} else if (cur_char == 's') {
EATWS();
char * endptr = 0;
double x = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t')) return false;
line = endptr;
EATWS();
double y = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t')) return false;
line = endptr;
EATWS();
double z = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0)) return false;
line = endptr;
EATWS();
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->Ks[0] = x;
data.new_mtl_unmap[cur_mtl_name]->Ks[1] = y;
data.new_mtl_unmap[cur_mtl_name]->Ks[2] = z;
}
} else if (cur_char == 'e') {
EATWS();
char * endptr = 0;
double x = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t')) return false;
line = endptr;
EATWS();
double y = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t')) return false;
line = endptr;
EATWS();
double z = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0)) return false;
line = endptr;
EATWS();
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->Ke[0] = x;
data.new_mtl_unmap[cur_mtl_name]->Ke[1] = y;
data.new_mtl_unmap[cur_mtl_name]->Ke[2] = z;
}
}
break;
}
case 'i': {
if (*(line++) != 'l' || *(line++) != 'l' || *(line++) != 'u' || *(line++) != 'm')
return false;
EATWS();
char * endptr = 0;
double illum = strtod(line, &endptr);
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->illum = (float) illum;
}
break;
}
case 'd': {
EATWS();
char * endptr = 0;
double d = strtod(line, &endptr);
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->d = (float) d;
}
break;
}
case 'T': {
if (*(line++) != 'f')
return false;
EATWS();
char * endptr = 0;
double x = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
return false;
line = endptr;
EATWS();
double y = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
return false;
line = endptr;
EATWS();
double z = strtod(line, &endptr);
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
return false;
line = endptr;
EATWS();
if (data.new_mtl_unmap.find(cur_mtl_name) != data.new_mtl_unmap.end()) {
data.new_mtl_unmap[cur_mtl_name]->Tf[0] = x;
data.new_mtl_unmap[cur_mtl_name]->Tf[1] = y;
data.new_mtl_unmap[cur_mtl_name]->Tf[2] = z;
}
break;
}
}
return true;
}
bool objparse(const char *path, ObjData &data) bool objparse(const char *path, ObjData &data)
{ {
@ -369,6 +576,48 @@ bool objparse(const char *path, ObjData &data)
return true; return true;
} }
bool mtlparse(const char *path, MtlData &data)
{
Slic3r::CNumericLocalesSetter locales_setter;
FILE *pFile = boost::nowide::fopen(path, "rt");
if (pFile == 0) return false;
cur_mtl_name = "";
try {
char buf[65536 * 2];
size_t len = 0;
size_t lenPrev = 0;
while ((len = ::fread(buf + lenPrev, 1, 65536, pFile)) != 0) {
len += lenPrev;
size_t lastLine = 0;
for (size_t i = 0; i < len; ++i)
if (buf[i] == '\r' || buf[i] == '\n') {
buf[i] = 0;
char *c = buf + lastLine;
while (*c == ' ' || *c == '\t') ++c;
// FIXME check the return value and exit on error?
// Will it break parsing of some obj files?
mtl_parseline(c, data);
lastLine = i + 1;
}
lenPrev = len - lastLine;
if (lenPrev > 65536) {
BOOST_LOG_TRIVIAL(error) << "MtlParser: Excessive line length";
::fclose(pFile);
return false;
}
memmove(buf, buf + lastLine, lenPrev);
}
} catch (std::bad_alloc &) {
BOOST_LOG_TRIVIAL(error) << "MtlParser: Out of memory";
}
::fclose(pFile);
// printf("vertices: %d\r\n", data.vertices.size() / 4);
// printf("coords: %d\r\n", data.coordinates.size());
return true;
}
bool objparse(std::istream &stream, ObjData &data) bool objparse(std::istream &stream, ObjData &data)
{ {
Slic3r::CNumericLocalesSetter locales_setter; Slic3r::CNumericLocalesSetter locales_setter;

View File

@ -3,6 +3,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <array>
#include <unordered_map>
#include <istream> #include <istream>
namespace ObjParser { namespace ObjParser {
@ -16,8 +18,7 @@ struct ObjVertex
inline bool operator==(const ObjVertex &v1, const ObjVertex &v2) inline bool operator==(const ObjVertex &v1, const ObjVertex &v2)
{ {
return return v1.coordIdx == v2.coordIdx &&
v1.coordIdx == v2.coordIdx &&
v1.textureCoordIdx == v2.textureCoordIdx && v1.textureCoordIdx == v2.textureCoordIdx &&
v1.normalIdx == v2.normalIdx; v1.normalIdx == v2.normalIdx;
} }
@ -25,13 +26,28 @@ inline bool operator==(const ObjVertex &v1, const ObjVertex &v2)
struct ObjUseMtl struct ObjUseMtl
{ {
int vertexIdxFirst; int vertexIdxFirst;
int vertexIdxEnd{-1};
std::string name; std::string name;
}; };
struct ObjNewMtl
{
std::string name;
float Ns;
float Ni;
float d;
float illum;
std::array<float, 3> Tf;
std::array<float, 3> Ka;
std::array<float, 3> Kd;
std::array<float, 3> Ks;
std::array<float, 3> Ke;
std::string map_Kd;//defalut png
};
inline bool operator==(const ObjUseMtl &v1, const ObjUseMtl &v2) inline bool operator==(const ObjUseMtl &v1, const ObjUseMtl &v2)
{ {
return return v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.name.compare(v2.name) == 0; v1.name.compare(v2.name) == 0;
} }
@ -56,8 +72,7 @@ struct ObjGroup
inline bool operator==(const ObjGroup &v1, const ObjGroup &v2) inline bool operator==(const ObjGroup &v1, const ObjGroup &v2)
{ {
return return v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.name.compare(v2.name) == 0; v1.name.compare(v2.name) == 0;
} }
@ -69,17 +84,19 @@ struct ObjSmoothingGroup
inline bool operator==(const ObjSmoothingGroup &v1, const ObjSmoothingGroup &v2) inline bool operator==(const ObjSmoothingGroup &v1, const ObjSmoothingGroup &v2)
{ {
return return v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.vertexIdxFirst == v2.vertexIdxFirst &&
v1.smoothingGroupID == v2.smoothingGroupID; v1.smoothingGroupID == v2.smoothingGroupID;
} }
#define OBJ_VERTEX_COLOR_ALPHA 6
#define OBJ_VERTEX_LENGTH 7 // x, y, z, color_x,color_y,color_z,color_w
#define ONE_FACE_SIZE 4//ONE_FACE format: f 8/4/6 7/3/6 6/2/6 -1/-1/-1
struct ObjData { struct ObjData {
// Version of the data structure for load / store in the private binary format. // Version of the data structure for load / store in the private binary format.
int version; int version;
// x, y, z, w // x, y, z, color_x,color_y,color_z,color_w
std::vector<float> coordinates; std::vector<float> coordinates;
bool has_vertex_color{false};
// u, v, w // u, v, w
std::vector<float> textureCoordinates; std::vector<float> textureCoordinates;
// x, y, z // x, y, z
@ -97,7 +114,14 @@ struct ObjData {
std::vector<ObjVertex> vertices; std::vector<ObjVertex> vertices;
}; };
struct MtlData
{
// Version of the data structure for load / store in the private binary format.
int version;
std::unordered_map<std::string, std::shared_ptr<ObjNewMtl>> new_mtl_unmap;
};
extern bool objparse(const char *path, ObjData &data); extern bool objparse(const char *path, ObjData &data);
extern bool mtlparse(const char *path, MtlData &data);
extern bool objparse(std::istream &stream, ObjData &data); extern bool objparse(std::istream &stream, ObjData &data);
extern bool objbinsave(const char *path, const ObjData &data); extern bool objbinsave(const char *path, const ObjData &data);

View File

@ -12,9 +12,6 @@
#include "TriangleSelector.hpp" #include "TriangleSelector.hpp"
#include "Format/AMF.hpp" #include "Format/AMF.hpp"
#include "Format/OBJ.hpp"
#include "Format/STL.hpp"
#include "Format/STEP.hpp"
#include "Format/svg.hpp" #include "Format/svg.hpp"
// BBS // BBS
#include "FaceDetector.hpp" #include "FaceDetector.hpp"
@ -50,6 +47,9 @@
#define _L(s) Slic3r::I18N::translate(s) #define _L(s) Slic3r::I18N::translate(s)
namespace Slic3r { namespace Slic3r {
const std::vector<std::string> CONST_FILAMENTS = {
"", "4", "8", "0C", "1C", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "AC", "BC", "CC", "DC",
}; // 5 10 15 16
// BBS initialization of static variables // BBS initialization of static variables
std::map<size_t, ExtruderParams> Model::extruderParamsMap = { {0,{"",0,0}}}; std::map<size_t, ExtruderParams> Model::extruderParamsMap = { {0,{"",0,0}}};
GlobalSpeedMap Model::printSpeedMap{}; GlobalSpeedMap Model::printSpeedMap{};
@ -172,7 +172,12 @@ Model::~Model()
// 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. // 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, 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, 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) ImportstlProgressFn stlFn,
ImportStepProgressFn stepFn,
StepIsUtf8Fn stepIsUtf8Fn,
BBLProject * project,
int plate_id,
ObjImportColorFn objFn)
{ {
Model model; Model model;
@ -203,8 +208,49 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
result = load_stl(input_file.c_str(), &model, nullptr, stlFn); result = load_stl(input_file.c_str(), &model, nullptr, stlFn);
else if (boost::algorithm::iends_with(input_file, ".oltp")) else if (boost::algorithm::iends_with(input_file, ".oltp"))
result = load_stl(input_file.c_str(), &model, nullptr, stlFn,256); result = load_stl(input_file.c_str(), &model, nullptr, stlFn,256);
else if (boost::algorithm::iends_with(input_file, ".obj")) else if (boost::algorithm::iends_with(input_file, ".obj")) {
result = load_obj(input_file.c_str(), &model, message); ObjInfo obj_info;
result = load_obj(input_file.c_str(), &model, obj_info, message);
if (result){
unsigned char first_extruder_id;
if (obj_info.vertex_colors.size() > 0) {
std::vector<unsigned char> vertex_filament_ids;
if (objFn) { // 1.result is ok and pop up a dialog
objFn(obj_info.vertex_colors, false, vertex_filament_ids, first_extruder_id);
if (vertex_filament_ids.size() > 0) {
result = obj_import_vertex_color_deal(vertex_filament_ids, first_extruder_id, & model);
}
} else { // test //todo delete
vertex_filament_ids.push_back(2);
vertex_filament_ids.push_back(3);
vertex_filament_ids.push_back(4);
vertex_filament_ids.push_back(1); // 4
vertex_filament_ids.push_back(1);
vertex_filament_ids.push_back(1);
vertex_filament_ids.push_back(1);
vertex_filament_ids.push_back(1); // 8
result = obj_import_vertex_color_deal(vertex_filament_ids, first_extruder_id, &model);
}
} else if (obj_info.face_colors.size() > 0 && obj_info.has_uv_png == false) { // mtl file
std::vector<unsigned char> face_filament_ids;
if (objFn) { // 1.result is ok and pop up a dialog
objFn(obj_info.face_colors, obj_info.is_single_mtl, face_filament_ids, first_extruder_id);
if (face_filament_ids.size() > 0) {
result = obj_import_face_color_deal(face_filament_ids, first_extruder_id, &model);
}
}
} else if (obj_info.has_uv_png && obj_info.uvs.size() > 0) {
boost::filesystem::path full_path(input_file);
std::string obj_directory = full_path.parent_path().string();
obj_info.obj_dircetory = obj_directory;
result = false;
message = _L("Importing obj with png function is developing.");
} else {
result = false;
message = _L("Importing obj occurred an unknown error.");
}
}
}
else if (boost::algorithm::iends_with(input_file, ".svg")) else if (boost::algorithm::iends_with(input_file, ".svg"))
result = load_svg(input_file.c_str(), &model, message); result = load_svg(input_file.c_str(), &model, message);
//BBS: remove the old .amf.xml files //BBS: remove the old .amf.xml files
@ -3394,6 +3440,163 @@ void Model::setExtruderParams(const DynamicPrintConfig& config, int extruders_co
} }
} }
static void get_real_filament_id(const unsigned char &id, std::string &result) {
if (id < CONST_FILAMENTS.size()) {
result = CONST_FILAMENTS[id];
} else {
result = "";//error
}
};
bool Model::obj_import_vertex_color_deal(const std::vector<unsigned char> &vertex_filament_ids, const unsigned char &first_extruder_id, Model *model)
{
if (vertex_filament_ids.size() == 0) {
return false;
}
// 2.generate mmu_segmentation_facets
if (model->objects.size() == 1 ) {
auto obj = model->objects[0];
obj->config.set("extruder", first_extruder_id);
if (obj->volumes.size() == 1) {
enum VertexColorCase {
_3_SAME_COLOR,
_3_DIFF_COLOR,
_2_SAME_1_DIFF_COLOR,
};
auto calc_vertex_color_case = [](const unsigned char &c0, const unsigned char &c1, const unsigned char &c2, VertexColorCase &vertex_color_case,
unsigned char &iso_index) {
if (c0 == c1 && c1 == c2) {
vertex_color_case = VertexColorCase::_3_SAME_COLOR;
} else if (c0 != c1 && c1 != c2 && c0 != c2) {
vertex_color_case = VertexColorCase::_3_DIFF_COLOR;
} else if (c0 == c1) {
vertex_color_case = _2_SAME_1_DIFF_COLOR;
iso_index = 2;
} else if (c1 == c2) {
vertex_color_case = _2_SAME_1_DIFF_COLOR;
iso_index = 0;
} else if (c0 == c2) {
vertex_color_case = _2_SAME_1_DIFF_COLOR;
iso_index = 1;
} else {
std::cout << "error";
}
};
auto calc_tri_area = [](const Vec3f &v0, const Vec3f &v1, const Vec3f &v2) {
return std::abs((v0 - v1).cross(v0 - v2).norm()) / 2;
};
auto volume = obj->volumes[0];
volume->config.set("extruder", first_extruder_id);
auto face_count = volume->mesh().its.indices.size();
volume->mmu_segmentation_facets.reserve(face_count);
if (volume->mesh().its.vertices.size() != vertex_filament_ids.size()) {
return false;
}
for (size_t i = 0; i < volume->mesh().its.indices.size(); i++) {
auto face = volume->mesh().its.indices[i];
auto filament_id0 = vertex_filament_ids[face[0]];
auto filament_id1 = vertex_filament_ids[face[1]];
auto filament_id2 = vertex_filament_ids[face[2]];
if (filament_id0 <= 1 && filament_id1 <= 1 && filament_id2 <= 2) {
continue;
}
if (i == 0) {
std::cout << "";
}
VertexColorCase vertex_color_case;
unsigned char iso_index;
calc_vertex_color_case(filament_id0, filament_id1, filament_id2, vertex_color_case, iso_index);
switch (vertex_color_case) {
case _3_SAME_COLOR: {
std::string result;
get_real_filament_id(filament_id0, result);
volume->mmu_segmentation_facets.set_triangle_from_string(i, result);
break;
}
case _3_DIFF_COLOR: {
std::string result0, result1, result2;
get_real_filament_id(filament_id0, result0);
get_real_filament_id(filament_id1, result1);
get_real_filament_id(filament_id2, result2);
auto v0 = volume->mesh().its.vertices[face[0]];
auto v1 = volume->mesh().its.vertices[face[1]];
auto v2 = volume->mesh().its.vertices[face[2]];
auto dir_0_1 = (v1 - v0).normalized();
auto dir_0_2 = (v2 - v0).normalized();
float sita0 = acos(dir_0_1.dot(dir_0_2));
auto dir_1_0 = -dir_0_1;
auto dir_1_2 = (v2 - v1).normalized();
float sita1 = acos(dir_1_0.dot(dir_1_2));
float sita2 = PI - sita0 - sita1;
std::array<float, 3> sitas = {sita0, sita1, sita2};
float max_sita = sitas[0];
int max_sita_vertex_index = 0;
for (size_t j = 1; j < sitas.size(); j++) {
if (sitas[j] > max_sita) {
max_sita_vertex_index = j;
max_sita = sitas[j];
}
}
if (max_sita_vertex_index == 0) {
volume->mmu_segmentation_facets.set_triangle_from_string(i, result0 + result1 + result2 + (result1 + result2 + "5" )+ "3"); //"1C0C2C0C1C13"
} else if (max_sita_vertex_index == 1) {
volume->mmu_segmentation_facets.set_triangle_from_string(i, result0 + result1 + result2 + (result0 + result2 + "9") + "3");
} else{// if (max_sita_vertex_index == 2)
volume->mmu_segmentation_facets.set_triangle_from_string(i, result0 + result1 + result2 + (result1 + result0 + "1") + "3");
}
break;
}
case _2_SAME_1_DIFF_COLOR: {
std::string result0, result1, result2;
get_real_filament_id(filament_id0, result0);
get_real_filament_id(filament_id1, result1);
get_real_filament_id(filament_id2, result2);
if (iso_index == 0) {
volume->mmu_segmentation_facets.set_triangle_from_string(i, result0 + result1 + result1 + "2");
} else if (iso_index == 1) {
volume->mmu_segmentation_facets.set_triangle_from_string(i, result1 + result0 + result0 + "6");
} else if (iso_index == 2) {
volume->mmu_segmentation_facets.set_triangle_from_string(i, result2 + result0 + result0 + "A");
}
break;
}
default: break;
}
}
return true;
}
}
return false;
}
bool Model::obj_import_face_color_deal(const std::vector<unsigned char> &face_filament_ids, const unsigned char &first_extruder_id, Model *model)
{
if (face_filament_ids.size() == 0) { return false; }
// 2.generate mmu_segmentation_facets
if (model->objects.size() == 1) {
auto obj = model->objects[0];
obj->config.set("extruder", first_extruder_id);
if (obj->volumes.size() == 1) {
auto volume = obj->volumes[0];
volume->config.set("extruder", first_extruder_id);
auto face_count = volume->mesh().its.indices.size();
volume->mmu_segmentation_facets.reserve(face_count);
if (volume->mesh().its.indices.size() != face_filament_ids.size()) { return false; }
for (size_t i = 0; i < volume->mesh().its.indices.size(); i++) {
auto face = volume->mesh().its.indices[i];
auto filament_id = face_filament_ids[i];
if (filament_id <= 1) { continue; }
std::string result;
get_real_filament_id(filament_id, result);
volume->mmu_segmentation_facets.set_triangle_from_string(i, result);
}
return true;
}
}
return false;
}
// update the maxSpeed of an object if it is different from the global configuration // update the maxSpeed of an object if it is different from the global configuration
double Model::findMaxSpeed(const ModelObject* object) { double Model::findMaxSpeed(const ModelObject* object) {
auto objectKeys = object->config.keys(); auto objectKeys = object->config.keys();

View File

@ -20,6 +20,7 @@
#include "Format/STEP.hpp" #include "Format/STEP.hpp"
//BBS: add stl //BBS: add stl
#include "Format/STL.hpp" #include "Format/STL.hpp"
#include "Format/OBJ.hpp"
#include "Calib.hpp" #include "Calib.hpp"
@ -1569,8 +1570,16 @@ public:
DynamicPrintConfig* config = nullptr, ConfigSubstitutionContext* config_substitutions = nullptr, DynamicPrintConfig* config = nullptr, ConfigSubstitutionContext* config_substitutions = nullptr,
LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr, 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, 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); ImportstlProgressFn stlFn = nullptr,
ImportStepProgressFn stepFn = nullptr,
StepIsUtf8Fn stepIsUtf8Fn = nullptr,
BBLProject * project = nullptr,
int plate_id = 0,
ObjImportColorFn objFn = nullptr
);
// BBS // BBS
static bool obj_import_vertex_color_deal(const std::vector<unsigned char> &vertex_filament_ids, const unsigned char &first_extruder_id, Model *model);
static bool obj_import_face_color_deal(const std::vector<unsigned char> &face_filament_ids, const unsigned char &first_extruder_id, Model *model);
static double findMaxSpeed(const ModelObject* object); static double findMaxSpeed(const ModelObject* object);
static double getThermalLength(const ModelVolume* modelVolumePtr); static double getThermalLength(const ModelVolume* modelVolumePtr);
static double getThermalLength(const std::vector<ModelVolume*> modelVolumePtrs); static double getThermalLength(const std::vector<ModelVolume*> modelVolumePtrs);

View File

@ -0,0 +1,187 @@
#pragma once
#include <iostream>
#include <ctime>
#include "opencv2/opencv.hpp"
class QuantKMeans
{
public:
int m_alpha_thres;
cv::Mat m_flatten_labels;
cv::Mat m_centers8UC3;
QuantKMeans(int alpha_thres = 10) : m_alpha_thres(alpha_thres) {}
void apply(cv::Mat &ori_image, cv::Mat &new_image, int num_cluster, int color_space)
{
cv::Mat image;
convert_color_space(ori_image, image, color_space);
cv::Mat flatten_image = flatten(image);
apply(flatten_image, num_cluster, color_space);
replace_centers(ori_image, new_image);
}
void apply_aplha(cv::Mat &ori_image, cv::Mat &new_image, int num_cluster, int color_space)
{
// cout << " *** DoAlpha *** " << endl;
cv::Mat flatten_image8UC3 = flatten_alpha(ori_image);
cv::Mat image8UC3;
convert_color_space(flatten_image8UC3, image8UC3, color_space);
cv::Mat image32FC3(image8UC3.rows, 1, CV_32FC3);
for (int i = 0; i < image8UC3.rows; i++) image32FC3.at<cv::Vec3f>(i, 0) = image8UC3.at<cv::Vec3b>(i, 0);
apply(image32FC3, num_cluster, color_space);
repalce_centers_aplha(ori_image, new_image);
}
void apply(cv::Mat &flatten_image, int num_cluster, int color_space)
{
cv::Mat centers32FC3;
num_cluster = fmin(flatten_image.rows, num_cluster);
kmeans(flatten_image, num_cluster, this->m_flatten_labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 300, 0.5), 3, cv::KMEANS_PP_CENTERS,
centers32FC3);
this->m_centers8UC3 = cv::Mat(num_cluster, 1, CV_8UC3);
for (int i = 0; i < num_cluster; i++) this->m_centers8UC3.at<cv::Vec3b>(i) = centers32FC3.at<cv::Vec3f>(i);
convert_color_space(this->m_centers8UC3, this->m_centers8UC3, color_space, true);
}
void apply(const std::vector<std::array<float, 4>> &ori_colors,
std::vector<std::array<float, 4>> & cluster_results,
std::vector<int> & labels,
int num_cluster = -1,
int color_space = 2)
{
// 0~255
cv::Mat flatten_image8UC3 = flatten_vector(ori_colors);
cv::Mat image8UC3;
convert_color_space(flatten_image8UC3, image8UC3, color_space);
cv::Mat image32FC3(image8UC3.rows, 1, CV_32FC3);
for (int i = 0; i < image8UC3.rows; i++) image32FC3.at<cv::Vec3f>(i, 0) = image8UC3.at<cv::Vec3b>(i, 0);
int best_cluster = 1, cur_score = 0, best_score = 100;
int max_cluster = ori_colors.size();
num_cluster = fmin(num_cluster, max_cluster);
if (num_cluster < 1) {
cur_score = kmeans(image32FC3, 1, this->m_flatten_labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 300, 0.5), 3, cv::KMEANS_PP_CENTERS);
best_score = cur_score;
for (int cur_cluster = 2; cur_cluster < 16; cur_cluster++) {
if (cur_cluster > max_cluster) break;
cur_score = kmeans(image32FC3, cur_cluster, this->m_flatten_labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 300, 0.5), 3,
cv::KMEANS_PP_CENTERS);
best_cluster = cur_score < best_score ? cur_cluster : best_cluster;
best_score = cur_score < best_score ? cur_score : best_score;
}
} else
best_cluster = num_cluster;
cv::Mat centers32FC3;
kmeans(image32FC3, best_cluster, this->m_flatten_labels, cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 300, 0.5), 3, cv::KMEANS_PP_CENTERS,
centers32FC3);
this->m_centers8UC3 = cv::Mat(best_cluster, 1, CV_8UC3);
for (int i = 0; i < best_cluster; i++) this->m_centers8UC3.at<cv::Vec3b>(i) = centers32FC3.at<cv::Vec3f>(i);
convert_color_space(this->m_centers8UC3, this->m_centers8UC3, color_space, true);
cluster_results.clear();
labels.clear();
for (int i = 0; i < ori_colors.size(); i++)
labels.emplace_back(this->m_flatten_labels.at<int>(i, 0));
for (int i = 0; i < best_cluster; i++) {
cv::Vec3f center = this->m_centers8UC3.at<cv::Vec3b>(i, 0);
cluster_results.emplace_back(std::array<float, 4>{center[0] / 255.f, center[1] / 255.f, center[2] / 255.f, 1.f});
}
}
void replace_centers(cv::Mat &ori_image, cv::Mat &new_image)
{
for (int i = 0; i < ori_image.rows; i++) {
for (int j = 0; j < ori_image.cols; j++) {
int idx = this->m_flatten_labels.at<int>(i * ori_image.cols + j, 0);
cv::Vec3b pixel = this->m_centers8UC3.at<cv::Vec3b>(idx);
new_image.at<cv::Vec3b>(i, j) = pixel;
}
}
}
void repalce_centers_aplha(cv::Mat &ori_image, cv::Mat &new_image)
{
int cnt = 0;
int idx;
cv::Vec3b center;
for (int i = 0; i < ori_image.rows; i++) {
for (int j = 0; j < ori_image.cols; j++) {
cv::Vec4b pixel = ori_image.at<cv::Vec4b>(i, j);
if ((int) pixel[3] < this->m_alpha_thres)
new_image.at<cv::Vec4b>(i, j) = pixel;
else {
idx = this->m_flatten_labels.at<int>(cnt++, 0);
center = this->m_centers8UC3.at<cv::Vec3b>(idx);
new_image.at<cv::Vec4b>(i, j) = cv::Vec4b(center[0], center[1], center[2], pixel[3]);
}
}
}
}
void convert_color_space(cv::Mat &ori_image, cv::Mat &image, int color_space, bool reverse = false)
{
switch (color_space) {
case 0: image = ori_image; break;
case 1:
if (reverse)
cvtColor(ori_image, image, cv::COLOR_HSV2BGR);
else
cvtColor(ori_image, image, cv::COLOR_BGR2HSV);
break;
case 2:
if (reverse)
cvtColor(ori_image, image, cv::COLOR_Lab2BGR);
else
cvtColor(ori_image, image, cv::COLOR_BGR2Lab);
break;
default: break;
}
}
cv::Mat flatten(cv::Mat &image)
{
int num_pixels = image.rows * image.cols;
cv::Mat img(num_pixels, 1, CV_32FC3);
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
cv::Vec3f pixel = image.at<cv::Vec3b>(i, j);
img.at<cv::Vec3f>(i * image.cols + j, 0) = pixel;
}
}
return img;
}
cv::Mat flatten_alpha(cv::Mat &image)
{
int num_pixels = image.rows * image.cols;
for (int i = 0; i < image.rows; i++)
for (int j = 0; j < image.cols; j++) {
cv::Vec4b pixel = image.at<cv::Vec4b>(i, j);
if ((int) pixel[3] < this->m_alpha_thres) num_pixels--;
}
cv::Mat img(num_pixels, 1, CV_8UC3);
int cnt = 0;
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
cv::Vec4b pixel = image.at<cv::Vec4b>(i, j);
if ((int) pixel[3] >= this->m_alpha_thres) img.at<cv::Vec3b>(cnt++, 0) = cv::Vec3b(pixel[0], pixel[1], pixel[2]);
}
}
return img;
}
cv::Mat flatten_vector(const std::vector<std::array<float, 4>> &ori_colors)
{
int num_pixels = ori_colors.size();
cv::Mat image8UC3(num_pixels, 1, CV_8UC3);
for (int i = 0; i < num_pixels; i++) {
std::array<float, 4> pixel = ori_colors[i];
image8UC3.at<cv::Vec3b>(i, 0) = cv::Vec3b((int) (pixel[0] * 255.f), (int) (pixel[1] * 255.f), (int) (pixel[2] * 255.f));
}
return image8UC3;
}
};

View File

@ -283,6 +283,8 @@ set(SLIC3R_GUI_SOURCES
GUI/CameraUtils.hpp GUI/CameraUtils.hpp
GUI/wxExtensions.cpp GUI/wxExtensions.cpp
GUI/wxExtensions.hpp GUI/wxExtensions.hpp
GUI/ObjColorDialog.cpp
GUI/ObjColorDialog.hpp
GUI/WipeTowerDialog.cpp GUI/WipeTowerDialog.cpp
GUI/WipeTowerDialog.hpp GUI/WipeTowerDialog.hpp
GUI/RemovableDriveManager.cpp GUI/RemovableDriveManager.cpp

View File

@ -0,0 +1,824 @@
#include <algorithm>
#include <sstream>
//#include "libslic3r/FlushVolCalc.hpp"
#include "ObjColorDialog.hpp"
#include "BitmapCache.hpp"
#include "GUI.hpp"
#include "I18N.hpp"
#include "GUI_App.hpp"
#include "MsgDialog.hpp"
#include "Widgets/Button.hpp"
#include "slic3r/Utils/ColorSpaceConvert.hpp"
#include "MainFrame.hpp"
#include "libslic3r/Config.hpp"
#include "BitmapComboBox.hpp"
#include "Widgets/ComboBox.hpp"
#include <wx/sizer.h>
#include "libslic3r/ObjColorUtils.hpp"
using namespace Slic3r;
using namespace Slic3r::GUI;
int objcolor_scale(const int val) { return val * Slic3r::GUI::wxGetApp().em_unit() / 10; }
int OBJCOLOR_ITEM_WIDTH() { return objcolor_scale(30); }
static const wxColour g_text_color = wxColour(107, 107, 107, 255);
const int HEADER_BORDER = 5;
const int CONTENT_BORDER = 3;
const int PANEL_WIDTH = 320;
const int COLOR_LABEL_WIDTH = 150;
#define ICON_SIZE wxSize(FromDIP(16), FromDIP(16))
#define MIN_OBJCOLOR_DIALOG_WIDTH FromDIP(530)
#define FIX_SCROLL_HEIGTH FromDIP(400)
#define BTN_SIZE wxSize(FromDIP(58), FromDIP(24))
#define BTN_GAP FromDIP(20)
static void update_ui(wxWindow* window)
{
Slic3r::GUI::wxGetApp().UpdateDarkUI(window);
}
static const char g_min_cluster_color = 1;
//static const char g_max_cluster_color = 15;
static const char g_max_color = 16;
const StateColor ok_btn_bg(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
const StateColor ok_btn_disable_bg(std::pair<wxColour, int>(wxColour(205, 201, 201), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(205, 201, 201), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(205, 201, 201), StateColor::Normal));
wxBoxSizer* ObjColorDialog::create_btn_sizer(long flags)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
btn_sizer->AddStretchSpacer();
StateColor ok_btn_bd(
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal)
);
StateColor ok_btn_text(
std::pair<wxColour, int>(wxColour(255, 255, 254), StateColor::Normal)
);
StateColor cancel_btn_bg(
std::pair<wxColour, int>(wxColour(206, 206, 206), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(238, 238, 238), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(255, 255, 255), StateColor::Normal)
);
StateColor cancel_btn_bd_(
std::pair<wxColour, int>(wxColour(38, 46, 48), StateColor::Normal)
);
StateColor cancel_btn_text(
std::pair<wxColour, int>(wxColour(38, 46, 48), StateColor::Normal)
);
StateColor calc_btn_bg(
std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed),
std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal)
);
StateColor calc_btn_bd(
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal)
);
StateColor calc_btn_text(
std::pair<wxColour, int>(wxColour(255, 255, 254), StateColor::Normal)
);
if (flags & wxOK) {
Button* ok_btn = new Button(this, _L("OK"));
ok_btn->SetMinSize(BTN_SIZE);
ok_btn->SetCornerRadius(FromDIP(12));
ok_btn->Enable(false);
ok_btn->SetBackgroundColor(ok_btn_disable_bg);
ok_btn->SetBorderColor(ok_btn_bd);
ok_btn->SetTextColor(ok_btn_text);
ok_btn->SetFocus();
ok_btn->SetId(wxID_OK);
btn_sizer->Add(ok_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, BTN_GAP);
m_button_list[wxOK] = ok_btn;
}
if (flags & wxCANCEL) {
Button* cancel_btn = new Button(this, _L("Cancel"));
cancel_btn->SetMinSize(BTN_SIZE);
cancel_btn->SetCornerRadius(FromDIP(12));
cancel_btn->SetBackgroundColor(cancel_btn_bg);
cancel_btn->SetBorderColor(cancel_btn_bd_);
cancel_btn->SetTextColor(cancel_btn_text);
cancel_btn->SetId(wxID_CANCEL);
btn_sizer->Add(cancel_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, BTN_GAP);
m_button_list[wxCANCEL] = cancel_btn;
}
return btn_sizer;
}
void ObjColorDialog::on_dpi_changed(const wxRect &suggested_rect)
{
for (auto button_item : m_button_list)
{
if (button_item.first == wxRESET)
{
button_item.second->SetMinSize(wxSize(FromDIP(75), FromDIP(24)));
button_item.second->SetCornerRadius(FromDIP(12));
}
if (button_item.first == wxOK) {
button_item.second->SetMinSize(BTN_SIZE);
button_item.second->SetCornerRadius(FromDIP(12));
}
if (button_item.first == wxCANCEL) {
button_item.second->SetMinSize(BTN_SIZE);
button_item.second->SetCornerRadius(FromDIP(12));
}
}
m_panel_ObjColor->msw_rescale();
this->Refresh();
};
ObjColorDialog::ObjColorDialog(wxWindow * parent,
std::vector<Slic3r::RGBA> & input_colors,
bool is_single_color,
const std::vector<std::string> &extruder_colours,
std::vector<unsigned char> & filament_ids,
unsigned char & first_extruder_id)
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe),
wxID_ANY,
_(L("Obj file Import color")),
wxDefaultPosition,
wxDefaultSize,
wxDEFAULT_DIALOG_STYLE /* | wxRESIZE_BORDER*/)
, m_filament_ids(filament_ids)
, m_first_extruder_id(first_extruder_id)
{
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));
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1));
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
this->SetBackgroundColour(*wxWHITE);
this->SetMinSize(wxSize(MIN_OBJCOLOR_DIALOG_WIDTH, -1));
m_panel_ObjColor = new ObjColorPanel(this, input_colors, is_single_color, extruder_colours, filament_ids, first_extruder_id);
auto main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
// set min sizer width according to extruders count
auto sizer_width = (int) (2.8 * OBJCOLOR_ITEM_WIDTH());
sizer_width = sizer_width > MIN_OBJCOLOR_DIALOG_WIDTH ? sizer_width : MIN_OBJCOLOR_DIALOG_WIDTH;
main_sizer->SetMinSize(wxSize(sizer_width, -1));
main_sizer->Add(m_panel_ObjColor, 1, wxEXPAND | wxALL, 0);
auto btn_sizer = create_btn_sizer(wxOK | wxCANCEL);
{
m_button_list[wxOK]->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent &e) {
if (m_panel_ObjColor->is_ok() == m_button_list[wxOK]->IsEnabled()) { return; }
m_button_list[wxOK]->Enable(m_panel_ObjColor->is_ok());
m_button_list[wxOK]->SetBackgroundColor(m_panel_ObjColor->is_ok() ? ok_btn_bg : ok_btn_disable_bg);
}));
}
main_sizer->Add(btn_sizer, 0, wxBOTTOM | wxRIGHT | wxEXPAND, BTN_GAP);
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
if (this->FindWindowById(wxID_OK, this)) {
this->FindWindowById(wxID_OK, this)->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {// if OK button is clicked..
m_panel_ObjColor->update_filament_ids();
EndModal(wxID_OK);
}, wxID_OK);
}
if (this->FindWindowById(wxID_CANCEL, this)) {
update_ui(static_cast<wxButton*>(this->FindWindowById(wxID_CANCEL, this)));
this->FindWindowById(wxID_CANCEL, this)->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { EndModal(wxCANCEL); });
}
this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
wxGetApp().UpdateDlgDarkUI(this);
}
RGBA convert_to_rgba(const wxColour &color)
{
RGBA rgba;
rgba[0] = std::clamp(color.Red() / 255.f, 0.f, 1.f);
rgba[1] = std::clamp(color.Green() / 255.f, 0.f, 1.f);
rgba[2] = std::clamp(color.Blue() / 255.f, 0.f, 1.f);
rgba[3] = std::clamp(color.Alpha() / 255.f, 0.f, 1.f);
return rgba;
}
wxColour convert_to_wxColour(const RGBA &color)
{
auto r = std::clamp((int) (color[0] * 255.f), 0, 255);
auto g = std::clamp((int) (color[1] * 255.f), 0, 255);
auto b = std::clamp((int) (color[2] * 255.f), 0, 255);
auto a = std::clamp((int) (color[3] * 255.f), 0, 255);
wxColour wx_color(r,g,b,a);
return wx_color;
}
// This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers)
ObjColorPanel::ObjColorPanel(wxWindow * parent,
std::vector<Slic3r::RGBA>& input_colors,
bool is_single_color,
const std::vector<std::string>& extruder_colours,
std::vector<unsigned char> & filament_ids,
unsigned char & first_extruder_id)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize /*,wxBORDER_RAISED*/)
, m_input_colors(input_colors)
, m_filament_ids(filament_ids)
, m_first_extruder_id(first_extruder_id)
{
if (input_colors.size() == 0) { return; }
for (const std::string& color : extruder_colours) {
m_colours.push_back(wxColor(color));
}
//deal input_colors
m_input_colors_size = input_colors.size();
for (size_t i = 0; i < input_colors.size(); i++) {
if (color_is_equal(input_colors[i] , UNDEFINE_COLOR)) { // not define color range:0~1
input_colors[i]=convert_to_rgba( m_colours[0]);
}
}
if (is_single_color && input_colors.size() >=1) {
m_cluster_colors_from_algo.emplace_back(input_colors[0]);
m_cluster_colours.emplace_back(convert_to_wxColour(input_colors[0]));
m_cluster_labels_from_algo.reserve(m_input_colors_size);
for (size_t i = 0; i < m_input_colors_size; i++) {
m_cluster_labels_from_algo.emplace_back(0);
}
m_cluster_map_filaments.resize(m_cluster_colors_from_algo.size());
m_color_num_recommend = m_color_cluster_num_by_algo = m_cluster_colors_from_algo.size();
} else {//cluster deal
deal_algo(-1);
}
//end first cluster
//draw ui
auto sizer_width = FromDIP(300);
// Create two switched panels with their own sizers
m_sizer_simple = new wxBoxSizer(wxVERTICAL);
m_page_simple = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
m_page_simple->SetSizer(m_sizer_simple);
m_page_simple->SetBackgroundColour(*wxWHITE);
update_ui(m_page_simple);
// BBS
m_sizer_simple->AddSpacer(FromDIP(10));
// BBS: for tunning flush volumes
{
//color cluster results
wxBoxSizer * cluster_sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText *color_cluster_result_title = new wxStaticText(m_page_simple, wxID_ANY, _L("Recommend number of colors:"));
color_cluster_result_title->SetFont(Label::Head_14);
cluster_sizer->Add(color_cluster_result_title, 0, wxALIGN_CENTER | wxALL, 0);
cluster_sizer->AddSpacer(FromDIP(5));
wxStaticText *color_cluster_result_value = new wxStaticText(m_page_simple, wxID_ANY, std::to_string(m_color_num_recommend).c_str());
color_cluster_result_value->SetFont(Label::Head_14);
cluster_sizer->Add(color_cluster_result_value, 0, wxALIGN_CENTER | wxALL, 0);
cluster_sizer->AddSpacer(FromDIP(20));
wxBoxSizer * specify_cluster_sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText *specify_color_cluster_title = new wxStaticText(m_page_simple, wxID_ANY, _L("Specify number of colors:"));
specify_color_cluster_title->SetFont(Label::Head_14);
specify_cluster_sizer->Add(specify_color_cluster_title, 0, wxALIGN_CENTER | wxALL, 0);
specify_cluster_sizer->AddSpacer(FromDIP(5));
m_color_cluster_num_by_user_ebox = new wxTextCtrl(m_page_simple, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(25), -1), wxTE_PROCESS_ENTER);
if (m_color_num_recommend == 1) {
m_color_cluster_num_by_user_ebox->Enable(false);
}
m_color_cluster_num_by_user_ebox->SetValue(std::to_string(m_color_cluster_num_by_algo).c_str());
{//event
auto on_apply_color_cluster_text_modify = [this](wxEvent &e) {
wxString str = m_color_cluster_num_by_user_ebox->GetValue();
int number = wxAtoi(str);
if (number > m_color_num_recommend || number < g_min_cluster_color) {
number = number < g_min_cluster_color ? g_min_cluster_color : m_color_num_recommend;
str = wxString::Format(("%d"), number);
m_color_cluster_num_by_user_ebox->SetValue(str);
MessageDialog dlg(nullptr, wxString::Format(_L("The color count should be in range [%d, %d]."), g_min_cluster_color, m_color_num_recommend),
_L("Warning"), wxICON_WARNING | wxOK);
dlg.ShowModal();
}
e.Skip();
};
m_color_cluster_num_by_user_ebox->Bind(wxEVT_TEXT_ENTER, on_apply_color_cluster_text_modify);
m_color_cluster_num_by_user_ebox->Bind(wxEVT_KILL_FOCUS, on_apply_color_cluster_text_modify);
m_color_cluster_num_by_user_ebox->Bind(wxEVT_COMMAND_TEXT_UPDATED, [this](wxCommandEvent &) {
wxString str = m_color_cluster_num_by_user_ebox->GetValue();
int number = wxAtof(str);
if (number > m_color_num_recommend || number < g_min_cluster_color) {
number = number < g_min_cluster_color ? g_min_cluster_color : m_color_num_recommend;
str = wxString::Format(("%d"), number);
m_color_cluster_num_by_user_ebox->SetValue(str);
m_color_cluster_num_by_user_ebox->SetInsertionPointEnd();
}
if (m_last_cluster_num != number) {
deal_algo(number, true);
Layout();
//Fit();
Refresh();
Update();
m_last_cluster_num = number;
}
});
m_color_cluster_num_by_user_ebox->Bind(wxEVT_CHAR, [this](wxKeyEvent &e) {
int keycode = e.GetKeyCode();
wxString input_char = wxString::Format("%c", keycode);
long value;
if (!input_char.ToLong(&value))
return;
e.Skip();
});
}
specify_cluster_sizer->Add(m_color_cluster_num_by_user_ebox, 0, wxALIGN_CENTER | wxALL, 0);
m_sizer_simple->Add(cluster_sizer, 0, wxEXPAND | wxLEFT, FromDIP(20));
m_sizer_simple->Add(specify_cluster_sizer, 0, wxEXPAND | wxLEFT, FromDIP(20));
//colors table
m_scrolledWindow = new wxScrolledWindow(m_page_simple,wxID_ANY,wxDefaultPosition,wxDefaultSize,wxSB_VERTICAL);
m_sizer_simple->Add(m_scrolledWindow, 0, wxEXPAND | wxALL, FromDIP(5));
draw_table();
//buttons
wxBoxSizer *quick_set_sizer = new wxBoxSizer(wxHORIZONTAL);
wxStaticText *quick_set_title = new wxStaticText(m_page_simple, wxID_ANY, _L("Quick set:"));
quick_set_sizer->Add(quick_set_title, 0, wxALIGN_CENTER | wxALL, 0);
quick_set_sizer->AddSpacer(FromDIP(10));
auto calc_approximate_match_btn_sizer = create_approximate_match_btn_sizer(m_page_simple);
auto calc_add_btn_sizer = create_add_btn_sizer(m_page_simple);
auto calc_reset_btn_sizer = create_reset_btn_sizer(m_page_simple);
quick_set_sizer->Add(calc_add_btn_sizer, 0, wxALIGN_CENTER | wxALL, 0);
quick_set_sizer->AddSpacer(FromDIP(10));
quick_set_sizer->Add(calc_approximate_match_btn_sizer, 0, wxALIGN_CENTER | wxALL, 0);
quick_set_sizer->AddSpacer(FromDIP(10));
quick_set_sizer->Add(calc_reset_btn_sizer, 0, wxALIGN_CENTER | wxALL, 0);
quick_set_sizer->AddSpacer(FromDIP(10));
m_sizer_simple->Add(quick_set_sizer, 0, wxEXPAND | wxLEFT, FromDIP(30));
wxBoxSizer *warning_sizer = new wxBoxSizer(wxHORIZONTAL);
m_warning_text = new wxStaticText(m_page_simple, wxID_ANY, "");
warning_sizer->Add(m_warning_text, 0, wxALIGN_CENTER | wxALL, 0);
m_sizer_simple->Add(warning_sizer, 0, wxEXPAND | wxLEFT, FromDIP(30));
m_sizer_simple->AddSpacer(10);
}
//page_simple//page_advanced
m_sizer = new wxBoxSizer(wxVERTICAL);
m_sizer->Add(m_page_simple, 0, wxEXPAND, 0);
m_sizer->SetSizeHints(this);
SetSizer(m_sizer);
this->Layout();
}
void ObjColorPanel::msw_rescale()
{
for (unsigned int i = 0; i < m_extruder_icon_list.size(); ++i) {
auto bitmap = *get_extruder_color_icon(m_colours[i].GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(i + 1), FromDIP(16), FromDIP(16));
m_extruder_icon_list[i]->SetBitmap(bitmap);
}
/* for (unsigned int i = 0; i < m_color_cluster_icon_list.size(); ++i) {
auto bitmap = *get_extruder_color_icon(m_cluster_colours[i].GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(i + 1), FromDIP(16), FromDIP(16));
m_color_cluster_icon_list[i]->SetBitmap(bitmap);
}*/
}
bool ObjColorPanel::is_ok() {
for (auto item : m_result_icon_list) {
if (item->bitmap_combox->IsShown()) {
auto selection = item->bitmap_combox->GetSelection();
if (selection < 1) {
return false;
}
}
}
return true;
}
void ObjColorPanel::update_filament_ids()
{
if (m_is_add_filament) {
for (auto c:m_new_add_colors) {
auto evt = new ColorEvent(EVT_ADD_CUSTOM_FILAMENT, c);
wxQueueEvent(wxGetApp().plater(), evt);
}
}
//deal m_filament_ids
m_filament_ids.clear();
m_filament_ids.reserve(m_input_colors_size);
for (size_t i = 0; i < m_input_colors_size; i++) {
auto label = m_cluster_labels_from_algo[i];
m_filament_ids.emplace_back(m_cluster_map_filaments[label]);
}
m_first_extruder_id = m_cluster_map_filaments[0];
}
wxBoxSizer *ObjColorPanel::create_approximate_match_btn_sizer(wxWindow *parent)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
StateColor calc_btn_bg(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
StateColor calc_btn_bd(std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
StateColor calc_btn_text(std::pair<wxColour, int>(wxColour(255, 255, 254), StateColor::Normal));
//create btn
m_quick_approximate_match_btn = new Button(parent, _L("Approximate match"));
auto cur_btn = m_quick_approximate_match_btn;
cur_btn->SetFont(Label::Body_13);
cur_btn->SetMinSize(wxSize(FromDIP(60), FromDIP(20)));
cur_btn->SetCornerRadius(FromDIP(10));
cur_btn->SetBackgroundColor(calc_btn_bg);
cur_btn->SetBorderColor(calc_btn_bd);
cur_btn->SetTextColor(calc_btn_text);
cur_btn->SetFocus();
btn_sizer->Add(cur_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 0);
cur_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
deal_approximate_match_btn();
});
return btn_sizer;
}
wxBoxSizer *ObjColorPanel::create_add_btn_sizer(wxWindow *parent)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
StateColor calc_btn_bg(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
StateColor calc_btn_bd(std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
StateColor calc_btn_text(std::pair<wxColour, int>(wxColour(255, 255, 254), StateColor::Normal));
// create btn
m_quick_add_btn = new Button(parent, _L("Add"));
auto cur_btn = m_quick_add_btn;
cur_btn->SetFont(Label::Body_13);
cur_btn->SetMinSize(wxSize(FromDIP(60), FromDIP(20)));
cur_btn->SetCornerRadius(FromDIP(10));
cur_btn->SetBackgroundColor(calc_btn_bg);
cur_btn->SetBorderColor(calc_btn_bd);
cur_btn->SetTextColor(calc_btn_text);
cur_btn->SetFocus();
btn_sizer->Add(cur_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 0);
cur_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
deal_add_btn();
});
return btn_sizer;
}
wxBoxSizer *ObjColorPanel::create_reset_btn_sizer(wxWindow *parent)
{
auto btn_sizer = new wxBoxSizer(wxHORIZONTAL);
StateColor calc_btn_bg(std::pair<wxColour, int>(wxColour(27, 136, 68), StateColor::Pressed), std::pair<wxColour, int>(wxColour(61, 203, 115), StateColor::Hovered),
std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
StateColor calc_btn_bd(std::pair<wxColour, int>(wxColour(0, 174, 66), StateColor::Normal));
StateColor calc_btn_text(std::pair<wxColour, int>(wxColour(255, 255, 254), StateColor::Normal));
// create btn
m_quick_reset_btn = new Button(parent, _L("Reset"));
auto cur_btn = m_quick_reset_btn;
cur_btn->SetFont(Label::Body_13);
cur_btn->SetMinSize(wxSize(FromDIP(60), FromDIP(20)));
cur_btn->SetCornerRadius(FromDIP(10));
cur_btn->SetBackgroundColor(calc_btn_bg);
cur_btn->SetBorderColor(calc_btn_bd);
cur_btn->SetTextColor(calc_btn_text);
cur_btn->SetFocus();
btn_sizer->Add(cur_btn, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 0);
cur_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
deal_reset_btn();
});
return btn_sizer;
}
wxBoxSizer *ObjColorPanel::create_extruder_icon_and_rgba_sizer(wxWindow *parent, int id, const wxColour &color)
{
auto icon_sizer = new wxBoxSizer(wxHORIZONTAL);
wxButton *icon = new wxButton(parent, wxID_ANY, {}, wxDefaultPosition, ICON_SIZE, wxBORDER_NONE | wxBU_AUTODRAW);
icon->SetBitmap(*get_extruder_color_icon(color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(id + 1), FromDIP(16), FromDIP(16)));
icon->SetCanFocus(false);
m_extruder_icon_list.emplace_back(icon);
icon_sizer->Add(icon, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0); // wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM
icon_sizer->AddSpacer(FromDIP(10));
std::string message = get_color_str(color);
wxStaticText *rgba_title = new wxStaticText(parent, wxID_ANY, message.c_str());
rgba_title->SetMinSize(wxSize(FromDIP(COLOR_LABEL_WIDTH), -1));
rgba_title->SetMaxSize(wxSize(FromDIP(COLOR_LABEL_WIDTH), -1));
icon_sizer->Add(rgba_title, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
return icon_sizer;
}
std::string ObjColorPanel::get_color_str(const wxColour &color) {
std::string str = ("R:" + std::to_string(color.Red()) +
std::string(" G:") + std::to_string(color.Green()) +
std::string(" B:") + std::to_string(color.Blue()) +
std::string(" A:") + std::to_string(color.Alpha()));
return str;
}
ComboBox *ObjColorPanel::CreateEditorCtrl(wxWindow *parent, int id) // wxRect labelRect,, const wxVariant &value
{
std::vector<wxBitmap *> icons = get_extruder_color_icons();
const double em = Slic3r::GUI::wxGetApp().em_unit();
bool thin_icon = false;
const int icon_width = lround((thin_icon ? 2 : 4.4) * em);
const int icon_height = lround(2 * em);
m_combox_icon_width = icon_width;
m_combox_icon_height = icon_height;
wxColour undefined_color(0,255,0,255);
icons.insert(icons.begin(), get_extruder_color_icon(undefined_color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(-1), icon_width, icon_height));
if (icons.empty())
return nullptr;
::ComboBox *c_editor = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(m_combox_width), -1), 0, nullptr,
wxCB_READONLY | CB_NO_DROP_ICON | CB_NO_TEXT);
c_editor->SetMinSize(wxSize(FromDIP(m_combox_width), -1));
c_editor->SetMaxSize(wxSize(FromDIP(m_combox_width), -1));
c_editor->GetDropDown().SetUseContentWidth(true);
for (size_t i = 0; i < icons.size(); i++) {
c_editor->Append(wxString::Format("%d", i), *icons[i]);
if (i == 0) {
c_editor->SetItemTooltip(i,undefined_color.GetAsString(wxC2S_HTML_SYNTAX));
} else {
c_editor->SetItemTooltip(i, m_colours[i-1].GetAsString(wxC2S_HTML_SYNTAX));
}
}
c_editor->SetSelection(0);
c_editor->SetName(wxString::Format("%d", id));
c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
auto *com_box = static_cast<ComboBox *>(evt.GetEventObject());
int i = atoi(com_box->GetName().c_str());
if (i < m_cluster_map_filaments.size()) { m_cluster_map_filaments[i] = com_box->GetSelection(); }
evt.StopPropagation();
});
return c_editor;
}
void ObjColorPanel::deal_approximate_match_btn()
{
auto calc_color_distance = [](wxColour c1, wxColour c2) {
float lab[2][3];
RGB2Lab(c1.Red(), c1.Green(), c1.Blue(), &lab[0][0], &lab[0][1], &lab[0][2]);
RGB2Lab(c2.Red(), c2.Green(), c2.Blue(), &lab[1][0], &lab[1][1], &lab[1][2]);
return DeltaE76(lab[0][0], lab[0][1], lab[0][2], lab[1][0], lab[1][1], lab[1][2]);
};
m_warning_text->SetLabelText("");
if (m_result_icon_list.size() == 0) { return; }
auto map_count = m_result_icon_list[0]->bitmap_combox->GetCount() -1;
if (map_count < 1) { return; }
for (size_t i = 0; i < m_cluster_colours.size(); i++) {
auto c = m_cluster_colours[i];
std::vector<ColorDistValue> color_dists;
color_dists.resize(map_count);
for (size_t j = 0; j < map_count; j++) {
auto tip_color = m_result_icon_list[0]->bitmap_combox->GetItemTooltip(j+1);
wxColour candidate_c(tip_color);
color_dists[j].distance = calc_color_distance(c, candidate_c);
color_dists[j].id = j + 1;
}
std::sort(color_dists.begin(), color_dists.end(), [](ColorDistValue &a, ColorDistValue& b) {
return a.distance < b.distance;
});
auto new_index= color_dists[0].id;
m_result_icon_list[i]->bitmap_combox->SetSelection(new_index);
m_cluster_map_filaments[i] = new_index;
}
}
void ObjColorPanel::show_sizer(wxSizer *sizer, bool show)
{
wxSizerItemList items = sizer->GetChildren();
for (wxSizerItemList::iterator it = items.begin(); it != items.end(); ++it) {
wxSizerItem *item = *it;
if (wxWindow *window = item->GetWindow()) {
window->Show(show);
}
if (wxSizer *son_sizer = item->GetSizer()) {
show_sizer(son_sizer, show);
}
}
}
void ObjColorPanel::redraw_part_table() {
//show all and set -1
deal_reset_btn();
for (size_t i = 0; i < m_row_sizer_list.size(); i++) {
show_sizer(m_row_sizer_list[i], true);
}
if (m_cluster_colours.size() < m_row_sizer_list.size()) { // show part
for (size_t i = m_cluster_colours.size(); i < m_row_sizer_list.size(); i++) {
show_sizer(m_row_sizer_list[i], false);
//m_row_panel_list[i]->Show(false); // show_sizer(m_left_color_cluster_boxsizer_list[i],false);
// m_result_icon_list[i]->bitmap_combox->Show(false);
}
} else if (m_cluster_colours.size() > m_row_sizer_list.size()) {
for (size_t i = m_row_sizer_list.size(); i < m_cluster_colours.size(); i++) {
int id = i;
wxPanel *row_panel = new wxPanel(m_scrolledWindow);
row_panel->SetBackgroundColour((i+1) % 2 == 0 ? *wxWHITE : wxColour(238, 238, 238));
auto row_sizer = new wxBoxSizer(wxHORIZONTAL);
row_panel->SetSizer(row_sizer);
wxPanel *son_row_panel = new wxPanel(row_panel);
son_row_panel->SetMinSize(wxSize(FromDIP(PANEL_WIDTH), -1));
son_row_panel->SetMaxSize(wxSize(FromDIP(PANEL_WIDTH), -1));
son_row_panel->SetBackgroundColour((i + 1) % 2 == 0 ? *wxWHITE : wxColour(238, 238, 238));
auto son_row_sizer = new wxGridSizer(1, 2, 1, 3);
son_row_panel->SetSizer(son_row_sizer);
auto cluster_color_icon_sizer = create_color_icon_and_rgba_sizer(son_row_panel, id, m_cluster_colours[id]);
son_row_sizer->Add(cluster_color_icon_sizer, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, FromDIP(CONTENT_BORDER));
// result_combox
create_result_button_sizer(son_row_panel, id);
son_row_sizer->Add(m_result_icon_list[id]->bitmap_combox, 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL, 0);
row_sizer->Add(son_row_panel, 0, wxALIGN_LEFT | wxALL, 0);
// extruder_icon
if (id < m_colours.size()) {
auto extruder_icon_sizer = create_extruder_icon_and_rgba_sizer(row_panel, id, m_colours[id]);
row_sizer->Add(extruder_icon_sizer, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, FromDIP(CONTENT_BORDER));
} else {
row_sizer->Add(new wxStaticText(row_panel, wxID_ANY, ""), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, FromDIP(CONTENT_BORDER));
}
m_row_sizer_list.emplace_back(son_row_sizer);
m_gridsizer->Add(row_panel, 0, wxALIGN_LEFT | wxALL, FromDIP(HEADER_BORDER));
}
m_gridsizer->Layout();
}
for (size_t i = 0; i < m_cluster_colours.size(); i++) { // update data
// m_color_cluster_icon_list//m_color_cluster_text_list
update_color_icon_and_rgba_sizer(i, m_cluster_colours[i]);
}
m_scrolledWindow->Refresh();
}
void ObjColorPanel::draw_table()
{
auto row = std::max(m_cluster_colours.size(), m_colours.size()) + 1;
m_gridsizer = new wxGridSizer(row, 1, 3, 3); //(int rows, int cols, int vgap, int hgap );
m_color_cluster_icon_list.clear();
m_extruder_icon_list.clear();
for (size_t ii = 0; ii < row; ii++) {
wxPanel *row_panel = new wxPanel(m_scrolledWindow);
row_panel->SetBackgroundColour(ii % 2 == 0 ? *wxWHITE : wxColour(238, 238, 238));
auto row_sizer = new wxBoxSizer(wxHORIZONTAL);
row_panel->SetSizer(row_sizer);
wxPanel *son_row_panel = new wxPanel(row_panel);
son_row_panel->SetMinSize(wxSize(FromDIP(PANEL_WIDTH), -1));
son_row_panel->SetMaxSize(wxSize(FromDIP(PANEL_WIDTH), -1));
son_row_panel->SetBackgroundColour(ii % 2 == 0 ? *wxWHITE : wxColour(238, 238, 238));
auto son_row_sizer = new wxGridSizer(1, 2, 1, 3);
son_row_panel->SetSizer(son_row_sizer);
if (ii == 0) {
wxStaticText *colors_left_title = new wxStaticText(son_row_panel, wxID_ANY, _L("Cluster colors"));
colors_left_title->SetFont(Label::Head_14);
son_row_sizer->Add(colors_left_title, 0, wxALIGN_LEFT | wxALL, FromDIP(HEADER_BORDER));
wxStaticText *colors_middle_title = new wxStaticText(son_row_panel, wxID_ANY, _L("Map Filament"));
colors_middle_title->SetFont(Label::Head_14);
son_row_sizer->Add(colors_middle_title, 0, wxALIGN_CENTER | wxALL, FromDIP(HEADER_BORDER));
row_sizer->Add(son_row_panel, 0, wxALIGN_LEFT | wxALL, 0);
wxStaticText *colors_right_title = new wxStaticText(row_panel, wxID_ANY, _L("Current filament colors"));
colors_right_title->SetFont(Label::Head_14);
row_sizer->Add(colors_right_title, 0, wxALIGN_LEFT | wxALL, FromDIP(HEADER_BORDER));
} else {
int id = ii - 1;
if (id < m_cluster_colours.size()) {
auto cluster_color_icon_sizer = create_color_icon_and_rgba_sizer(son_row_panel, id, m_cluster_colours[id]);
son_row_sizer->Add(cluster_color_icon_sizer, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, FromDIP(CONTENT_BORDER));
// result_combox
create_result_button_sizer(son_row_panel, id);
son_row_sizer->Add(m_result_icon_list[id]->bitmap_combox, 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL, FromDIP(CONTENT_BORDER));
}
row_sizer->Add(son_row_panel, 0, wxALIGN_LEFT | wxALL, 0);
// extruder_icon
if (id < m_colours.size()) {
auto extruder_icon_sizer = create_extruder_icon_and_rgba_sizer(row_panel, id, m_colours[id]);
row_sizer->Add(extruder_icon_sizer, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, FromDIP(CONTENT_BORDER));
} else {
row_sizer->Add(new wxStaticText(row_panel, wxID_ANY, ""), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, FromDIP(CONTENT_BORDER));
}
}
if (ii>=1) {
m_row_sizer_list.emplace_back(son_row_sizer);
}
m_gridsizer->Add(row_panel, 0, wxALIGN_LEFT | wxALL, FromDIP(HEADER_BORDER));
}
m_scrolledWindow->SetSizer(m_gridsizer);
int totalHeight = m_gridsizer->GetMinSize().y;
m_scrolledWindow->SetVirtualSize(MIN_OBJCOLOR_DIALOG_WIDTH, totalHeight);
if (totalHeight > FIX_SCROLL_HEIGTH) {
m_scrolledWindow->SetMinSize(wxSize(MIN_OBJCOLOR_DIALOG_WIDTH, FIX_SCROLL_HEIGTH));
m_scrolledWindow->SetMaxSize(wxSize(MIN_OBJCOLOR_DIALOG_WIDTH, FIX_SCROLL_HEIGTH));
}
else {
m_scrolledWindow->SetMinSize(wxSize(MIN_OBJCOLOR_DIALOG_WIDTH, totalHeight));
}
m_scrolledWindow->EnableScrolling(false, true);
m_scrolledWindow->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT);//wxSHOW_SB_ALWAYS
m_scrolledWindow->SetScrollRate(20, 20);
}
void ObjColorPanel::deal_algo(char cluster_number, bool redraw_ui)
{
if (m_last_cluster_number == cluster_number) {
return;
}
m_last_cluster_number = cluster_number;
QuantKMeans quant(10);
quant.apply(m_input_colors, m_cluster_colors_from_algo, m_cluster_labels_from_algo, (int)cluster_number);
m_cluster_colours.clear();
m_cluster_colours.reserve(m_cluster_colors_from_algo.size());
for (size_t i = 0; i < m_cluster_colors_from_algo.size(); i++) {
m_cluster_colours.emplace_back(convert_to_wxColour(m_cluster_colors_from_algo[i]));
}
if (m_cluster_colours.size() == 0) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ",m_cluster_colours.size() = 0\n";
return;
}
m_cluster_map_filaments.resize(m_cluster_colors_from_algo.size());
m_color_cluster_num_by_algo = m_cluster_colors_from_algo.size();
if (cluster_number == -1) {
m_color_num_recommend = m_color_cluster_num_by_algo;
}
//redraw ui
if (redraw_ui) {
redraw_part_table();
}
}
void ObjColorPanel::deal_add_btn()
{
if (m_colours.size() > g_max_color) { return; }
std::vector<wxBitmap *> new_icons;
auto new_color_size = m_cluster_colors_from_algo.size();
new_icons.reserve(new_color_size);
m_new_add_colors.clear();
m_new_add_colors.reserve(new_color_size);
int new_index = m_colours.size() + 1;
for (size_t i = 0; i < new_color_size; i++) {
if (m_colours.size() + new_icons.size() >= g_max_color) {
m_warning_text->SetLabelText(_L("Waring:The count of newly added and current extruders exceeds 16."));
break;
}
wxColour cur_color = convert_to_wxColour(m_cluster_colors_from_algo[i]);
m_new_add_colors.emplace_back(cur_color);
new_icons.emplace_back(get_extruder_color_icon(cur_color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(),
std::to_string(new_index), m_combox_icon_width, m_combox_icon_height));
new_index++;
}
new_index = m_colours.size() + 1;
for (size_t i = 0; i < m_result_icon_list.size(); i++) {
auto item = m_result_icon_list[i];
for (size_t k = 0; k < new_icons.size(); k++) {
item->bitmap_combox->Append(wxString::Format("%d", item->bitmap_combox->GetCount()), *new_icons[k]);
item->bitmap_combox->SetItemTooltip(item->bitmap_combox->GetCount() -1,m_new_add_colors[k].GetAsString(wxC2S_HTML_SYNTAX));
}
item->bitmap_combox->SetSelection(new_index);
m_cluster_map_filaments[i] = new_index;
new_index++;
}
m_is_add_filament = true;
}
void ObjColorPanel::deal_reset_btn()
{
for (auto item : m_result_icon_list) {
// delete redundant bitmap
while (item->bitmap_combox->GetCount() > m_colours.size()+ 1) {
item->bitmap_combox->DeleteOneItem(item->bitmap_combox->GetCount() - 1);
}
item->bitmap_combox->SetSelection(0);
}
m_is_add_filament = false;
m_new_add_colors.clear();
m_warning_text->SetLabelText("");
}
void ObjColorPanel::create_result_button_sizer(wxWindow *parent, int id)
{
for (size_t i = m_result_icon_list.size(); i < id + 1; i++) {
m_result_icon_list.emplace_back(new ButtonState());
}
m_result_icon_list[id]->bitmap_combox = CreateEditorCtrl(parent,id);
}
wxBoxSizer *ObjColorPanel::create_color_icon_and_rgba_sizer(wxWindow *parent, int id, const wxColour& color)
{
auto icon_sizer = new wxBoxSizer(wxHORIZONTAL);
wxButton *icon = new wxButton(parent, wxID_ANY, {}, wxDefaultPosition, ICON_SIZE, wxBORDER_NONE | wxBU_AUTODRAW);
icon->SetBitmap(*get_extruder_color_icon(color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(id + 1), FromDIP(16), FromDIP(16)));
icon->SetCanFocus(false);
m_color_cluster_icon_list.emplace_back(icon);
icon_sizer->Add(icon, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0); // wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM
icon_sizer->AddSpacer(FromDIP(10));
std::string message = get_color_str(color);
wxStaticText *rgba_title = new wxStaticText(parent, wxID_ANY, message.c_str());
m_color_cluster_text_list.emplace_back(rgba_title);
rgba_title->SetMinSize(wxSize(FromDIP(COLOR_LABEL_WIDTH), -1));
rgba_title->SetMaxSize(wxSize(FromDIP(COLOR_LABEL_WIDTH), -1));
//rgba_title->SetFont(Label::Head_12);
icon_sizer->Add(rgba_title, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
return icon_sizer;
}
void ObjColorPanel::update_color_icon_and_rgba_sizer(int id, const wxColour &color)
{
if (id < m_color_cluster_text_list.size()) {
auto icon = m_color_cluster_icon_list[id];
icon->SetBitmap(*get_extruder_color_icon(color.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(), std::to_string(id + 1), FromDIP(16), FromDIP(16)));
std::string message = get_color_str(color);
m_color_cluster_text_list[id]->SetLabelText(message.c_str());
}
}

View File

@ -0,0 +1,113 @@
#ifndef _OBJ_COLOR_DIALOG_H_
#define _OBJ_COLOR_DIALOG_H_
#include "GUI_Utils.hpp"
#include "libslic3r/Color.hpp"
#include <wx/sizer.h>
#include <wx/spinctrl.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include <wx/msgdlg.h>
class Button;
class Label;
class ComboBox;
struct ColorDistValue
{
int id;
float distance;
};
class ObjColorPanel : public wxPanel
{
public:
// BBS
ObjColorPanel(wxWindow * parent,
std::vector<Slic3r::RGBA> & input_colors,bool is_single_color,
const std::vector<std::string> & extruder_colours,
std::vector<unsigned char> & filament_ids,
unsigned char & first_extruder_id);
void msw_rescale();
bool is_ok();
void update_filament_ids();
struct ButtonState
{
ComboBox* bitmap_combox{nullptr};
bool is_map{false};//int id{0};
};
private:
wxBoxSizer *create_approximate_match_btn_sizer(wxWindow *parent);
wxBoxSizer *create_add_btn_sizer(wxWindow *parent);
wxBoxSizer *create_reset_btn_sizer(wxWindow *parent);
wxBoxSizer *create_extruder_icon_and_rgba_sizer(wxWindow *parent, int id, const wxColour& color);
std::string get_color_str(const wxColour &color);
void create_result_button_sizer(wxWindow *parent, int id);
wxBoxSizer *create_color_icon_and_rgba_sizer(wxWindow *parent, int id, const wxColour& color);
void update_color_icon_and_rgba_sizer(int id, const wxColour &color);
ComboBox* CreateEditorCtrl(wxWindow *parent,int id);
void draw_table();
void show_sizer(wxSizer *sizer, bool show);
void redraw_part_table();
void deal_approximate_match_btn();
void deal_add_btn();
void deal_reset_btn();
void deal_algo(char cluster_number,bool redraw_ui =false);
private:
//view ui
wxScrolledWindow * m_scrolledWindow = nullptr;
wxPanel * m_page_simple = nullptr;
wxBoxSizer * m_sizer = nullptr;
wxBoxSizer * m_sizer_simple = nullptr;
wxTextCtrl *m_color_cluster_num_by_user_ebox{nullptr};
wxStaticText * m_warning_text{nullptr};
Button * m_quick_approximate_match_btn{nullptr};
Button * m_quick_add_btn{nullptr};
Button * m_quick_reset_btn{nullptr};
std::vector<wxButton*> m_extruder_icon_list;
std::vector<wxButton*> m_color_cluster_icon_list;//need modeify
std::vector<wxStaticText*> m_color_cluster_text_list;//need modeify
std::vector<wxGridSizer*> m_row_sizer_list; // control show or not
std::vector<ButtonState*> m_result_icon_list;
int m_last_cluster_num{-1};
const int m_combox_width{50};
int m_combox_icon_width;
int m_combox_icon_height;
wxGridSizer* m_gridsizer = nullptr;
wxStaticText * m_test = nullptr;
//data
char m_last_cluster_number{-2};
std::vector<Slic3r::RGBA>& m_input_colors;
int m_color_num_recommend{0};
int m_color_cluster_num_by_algo{0};
int m_input_colors_size{0};
std::vector<wxColour> m_colours;//from project and show right
std::vector<int> m_cluster_map_filaments;//show middle
std::vector<wxColour> m_cluster_colours;//from_algo and show left
bool m_can_add_filament{true};
std::vector<wxColour> m_new_add_colors;
//algo result
std::vector<Slic3r::RGBA> m_cluster_colors_from_algo;
std::vector<int> m_cluster_labels_from_algo;
//result
bool m_is_add_filament{false};
unsigned char& m_first_extruder_id;
std::vector<unsigned char> &m_filament_ids;
};
class ObjColorDialog : public Slic3r::GUI::DPIDialog
{
public:
ObjColorDialog(wxWindow * parent,
std::vector<Slic3r::RGBA>& input_colors, bool is_single_color,
const std::vector<std::string> & extruder_colours,
std::vector<unsigned char>& filament_ids,
unsigned char & first_extruder_id);
wxBoxSizer* create_btn_sizer(long flags);
void on_dpi_changed(const wxRect &suggested_rect) override;
private:
ObjColorPanel* m_panel_ObjColor = nullptr;
std::unordered_map<int, Button *> m_button_list;
std::vector<unsigned char>& m_filament_ids;
unsigned char & m_first_extruder_id;
};
#endif // _WIPE_TOWER_DIALOG_H_

View File

@ -127,6 +127,7 @@
#include <wx/glcanvas.h> // Needs to be last because reasons :-/ #include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp" #include "WipeTowerDialog.hpp"
#include "ObjColorDialog.hpp"
#include "libslic3r/CustomGCode.hpp" #include "libslic3r/CustomGCode.hpp"
#include "libslic3r/Platform.hpp" #include "libslic3r/Platform.hpp"
@ -182,6 +183,7 @@ wxDEFINE_EVENT(EVT_CREATE_FILAMENT, SimpleEvent);
wxDEFINE_EVENT(EVT_MODIFY_FILAMENT, SimpleEvent); wxDEFINE_EVENT(EVT_MODIFY_FILAMENT, SimpleEvent);
wxDEFINE_EVENT(EVT_ADD_FILAMENT, SimpleEvent); wxDEFINE_EVENT(EVT_ADD_FILAMENT, SimpleEvent);
wxDEFINE_EVENT(EVT_DEL_FILAMENT, SimpleEvent); wxDEFINE_EVENT(EVT_DEL_FILAMENT, SimpleEvent);
wxDEFINE_EVENT(EVT_ADD_CUSTOM_FILAMENT, ColorEvent);
bool Plater::has_illegal_filename_characters(const wxString& wxs_name) bool Plater::has_illegal_filename_characters(const wxString& wxs_name)
{ {
std::string name = into_u8(wxs_name); std::string name = into_u8(wxs_name);
@ -1609,15 +1611,8 @@ void Sidebar::on_filaments_change(size_t num_filaments)
void Sidebar::add_filament() { void Sidebar::add_filament() {
// BBS: limit filament choices to 16 // BBS: limit filament choices to 16
if (p->combos_filament.size() >= 16) return; if (p->combos_filament.size() >= 16) return;
int filament_count = p->combos_filament.size() + 1;
wxColour new_col = Plater::get_next_color_for_filament(); wxColour new_col = Plater::get_next_color_for_filament();
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString(); add_custom_filament(new_col);
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
wxGetApp().plater()->on_filaments_change(filament_count);
wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
auto_calc_flushing_volumes(filament_count - 1);
} }
void Sidebar::delete_filament() { void Sidebar::delete_filament() {
@ -1638,6 +1633,18 @@ void Sidebar::delete_filament() {
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
} }
void Sidebar::add_custom_filament(wxColour new_col) {
if (p->combos_filament.size() >= 16) return;
int filament_count = p->combos_filament.size() + 1;
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
wxGetApp().preset_bundle->set_num_filaments(filament_count, new_color);
wxGetApp().plater()->on_filaments_change(filament_count);
wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
auto_calc_flushing_volumes(filament_count - 1);
}
void Sidebar::on_bed_type_change(BedType bed_type) void Sidebar::on_bed_type_change(BedType bed_type)
{ {
// btDefault option is not included in global bed type setting // btDefault option is not included in global bed type setting
@ -2552,6 +2559,7 @@ struct Plater::priv
void on_modify_filament(SimpleEvent &); void on_modify_filament(SimpleEvent &);
void on_add_filament(SimpleEvent &); void on_add_filament(SimpleEvent &);
void on_delete_filament(SimpleEvent &); void on_delete_filament(SimpleEvent &);
void on_add_custom_filament(ColorEvent &);
void on_object_select(SimpleEvent&); void on_object_select(SimpleEvent&);
void on_plate_name_change(SimpleEvent &); void on_plate_name_change(SimpleEvent &);
@ -2785,6 +2793,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->Bind(EVT_MODIFY_FILAMENT, &priv::on_modify_filament, this); this->q->Bind(EVT_MODIFY_FILAMENT, &priv::on_modify_filament, this);
this->q->Bind(EVT_ADD_FILAMENT, &priv::on_add_filament, this); this->q->Bind(EVT_ADD_FILAMENT, &priv::on_add_filament, this);
this->q->Bind(EVT_DEL_FILAMENT, &priv::on_delete_filament, this); this->q->Bind(EVT_DEL_FILAMENT, &priv::on_delete_filament, this);
this->q->Bind(EVT_ADD_CUSTOM_FILAMENT, &priv::on_add_custom_filament, this);
view3D = new View3D(q, bed, &model, config, &background_process); view3D = new View3D(q, bed, &model, config, &background_process);
//BBS: use partplater's gcode //BBS: use partplater's gcode
preview = new Preview(q, bed, &model, config, &background_process, partplate_list.get_current_slice_result(), [this]() { schedule_background_process(); }); preview = new Preview(q, bed, &model, config, &background_process, partplate_list.get_current_slice_result(), [this]() { schedule_background_process(); });
@ -3931,6 +3940,16 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
bool is_xxx; bool is_xxx;
Semver file_version; Semver file_version;
//ObjImportColorFn obj_color_fun=nullptr;
auto obj_color_fun = [this, &path](std::vector<RGBA> &input_colors, bool is_single_color, std::vector<unsigned char> &filament_ids,
unsigned char &first_extruder_id) {
if (!boost::iends_with(path.string(), ".obj")) { return; }
const std::vector<std::string> extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config();
ObjColorDialog color_dlg(nullptr, input_colors, is_single_color, extruder_colours, filament_ids, first_extruder_id);
if (color_dlg.ShowModal() != wxID_OK) {
filament_ids.clear();
}
};
model = Slic3r::Model::read_from_file( model = Slic3r::Model::read_from_file(
path.string(), nullptr, nullptr, strategy, &plate_data, &project_presets, &is_xxx, &file_version, nullptr, 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) [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)
@ -3960,7 +3979,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (!isUtf8StepFile) if (!isUtf8StepFile)
Slic3r::GUI::show_info(nullptr, _L("Name of components inside step file is not UTF8 format!") + "\n\n" + _L("The name may show garbage characters!"), Slic3r::GUI::show_info(nullptr, _L("Name of components inside step file is not UTF8 format!") + "\n\n" + _L("The name may show garbage characters!"),
_L("Attention!")); _L("Attention!"));
}); },
nullptr, 0, obj_color_fun);
if (designer_model_id.empty() && boost::algorithm::iends_with(path.string(), ".stl")) { if (designer_model_id.empty() && boost::algorithm::iends_with(path.string(), ".stl")) {
@ -8039,6 +8059,11 @@ void Plater::priv::on_delete_filament(SimpleEvent &evt) {
sidebar->delete_filament(); sidebar->delete_filament();
} }
void Plater::priv::on_add_custom_filament(ColorEvent &evt)
{
sidebar->add_custom_filament(evt.data);
}
void Plater::priv::enter_gizmos_stack() void Plater::priv::enter_gizmos_stack()
{ {
assert(m_undo_redo_stack_active == &m_undo_redo_stack_main); assert(m_undo_redo_stack_active == &m_undo_redo_stack_main);

View File

@ -101,6 +101,8 @@ wxDECLARE_EVENT(EVT_CREATE_FILAMENT, SimpleEvent);
wxDECLARE_EVENT(EVT_MODIFY_FILAMENT, SimpleEvent); wxDECLARE_EVENT(EVT_MODIFY_FILAMENT, SimpleEvent);
wxDECLARE_EVENT(EVT_ADD_FILAMENT, SimpleEvent); wxDECLARE_EVENT(EVT_ADD_FILAMENT, SimpleEvent);
wxDECLARE_EVENT(EVT_DEL_FILAMENT, SimpleEvent); wxDECLARE_EVENT(EVT_DEL_FILAMENT, SimpleEvent);
using ColorEvent = Event<wxColour>;
wxDECLARE_EVENT(EVT_ADD_CUSTOM_FILAMENT, ColorEvent);
const wxString DEFAULT_PROJECT_NAME = "Untitled"; const wxString DEFAULT_PROJECT_NAME = "Untitled";
class Sidebar : public wxPanel class Sidebar : public wxPanel
@ -136,6 +138,7 @@ public:
void on_filaments_change(size_t num_filaments); void on_filaments_change(size_t num_filaments);
void add_filament(); void add_filament();
void delete_filament(); void delete_filament();
void add_custom_filament(wxColour new_col);
// BBS // BBS
void on_bed_type_change(BedType bed_type); void on_bed_type_change(BedType bed_type);
void load_ams_list(std::string const & device, MachineObject* obj); void load_ams_list(std::string const & device, MachineObject* obj);

View File

@ -66,6 +66,7 @@ public:
wxBitmap GetItemBitmap(unsigned int n); wxBitmap GetItemBitmap(unsigned int n);
void SetItemBitmap(unsigned int n, wxBitmap const &bitmap); void SetItemBitmap(unsigned int n, wxBitmap const &bitmap);
bool is_drop_down(){return drop_down;} bool is_drop_down(){return drop_down;}
void DeleteOneItem(unsigned int pos) { DoDeleteOneItem(pos); }
protected: protected:
virtual int DoInsertItems(const wxArrayStringsAdapter &items, virtual int DoInsertItems(const wxArrayStringsAdapter &items,
unsigned int pos, unsigned int pos,

View File

@ -598,7 +598,6 @@ wxBitmap *get_extruder_color_icon(std::string color, std::string label, int icon
return bitmap; return bitmap;
} }
void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl, void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl,
wxWindow* parent, wxWindow* parent,
const std::string& first_item/* = ""*/, const std::string& first_item/* = ""*/,

View File

@ -75,7 +75,6 @@ wxBitmap create_scaled_bitmap(const std::string& bmp_name, wxWindow *win = nullp
wxBitmap* get_default_extruder_color_icon(bool thin_icon = false); wxBitmap* get_default_extruder_color_icon(bool thin_icon = false);
std::vector<wxBitmap *> get_extruder_color_icons(bool thin_icon = false); std::vector<wxBitmap *> get_extruder_color_icons(bool thin_icon = false);
wxBitmap * get_extruder_color_icon(std::string color, std::string label, int icon_width, int icon_height); wxBitmap * get_extruder_color_icon(std::string color, std::string label, int icon_width, int icon_height);
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {
class BitmapComboBox; class BitmapComboBox;