TriangleSelector: 3MF loading and saving
This commit is contained in:
parent
7ddb64783b
commit
248fba82a4
4 changed files with 78 additions and 0 deletions
|
@ -86,6 +86,7 @@ const char* OBJECTID_ATTR = "objectid";
|
||||||
const char* TRANSFORM_ATTR = "transform";
|
const char* TRANSFORM_ATTR = "transform";
|
||||||
const char* PRINTABLE_ATTR = "printable";
|
const char* PRINTABLE_ATTR = "printable";
|
||||||
const char* INSTANCESCOUNT_ATTR = "instances_count";
|
const char* INSTANCESCOUNT_ATTR = "instances_count";
|
||||||
|
const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
|
||||||
|
|
||||||
const char* KEY_ATTR = "key";
|
const char* KEY_ATTR = "key";
|
||||||
const char* VALUE_ATTR = "value";
|
const char* VALUE_ATTR = "value";
|
||||||
|
@ -283,6 +284,7 @@ namespace Slic3r {
|
||||||
{
|
{
|
||||||
std::vector<float> vertices;
|
std::vector<float> vertices;
|
||||||
std::vector<unsigned int> triangles;
|
std::vector<unsigned int> triangles;
|
||||||
|
std::vector<std::string> custom_supports;
|
||||||
|
|
||||||
bool empty()
|
bool empty()
|
||||||
{
|
{
|
||||||
|
@ -293,6 +295,7 @@ namespace Slic3r {
|
||||||
{
|
{
|
||||||
vertices.clear();
|
vertices.clear();
|
||||||
triangles.clear();
|
triangles.clear();
|
||||||
|
custom_supports.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1539,6 +1542,8 @@ namespace Slic3r {
|
||||||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
|
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
|
||||||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
|
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
|
||||||
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
|
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
|
||||||
|
|
||||||
|
m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1872,6 +1877,13 @@ namespace Slic3r {
|
||||||
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
|
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
|
||||||
volume->calculate_convex_hull();
|
volume->calculate_convex_hull();
|
||||||
|
|
||||||
|
// recreate custom supports from previously loaded attribute
|
||||||
|
assert(geometry.custom_supports.size() == triangles_count);
|
||||||
|
for (unsigned i=0; i<triangles_count; ++i) {
|
||||||
|
if (! geometry.custom_supports[i].empty())
|
||||||
|
volume->m_supported_facets.set_triangle_from_string(i, geometry.custom_supports[i]);
|
||||||
|
}
|
||||||
|
|
||||||
// apply the remaining volume's metadata
|
// apply the remaining volume's metadata
|
||||||
for (const Metadata& metadata : volume_data.metadata)
|
for (const Metadata& metadata : volume_data.metadata)
|
||||||
{
|
{
|
||||||
|
@ -2383,6 +2395,11 @@ namespace Slic3r {
|
||||||
{
|
{
|
||||||
stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" ";
|
stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string custom_supports_data_string = volume->m_supported_facets.get_triangle_as_string(i);
|
||||||
|
if (! custom_supports_data_string.empty())
|
||||||
|
stream << CUSTOM_SUPPORTS_ATTR << "=\"" << custom_supports_data_string << "\" ";
|
||||||
|
|
||||||
stream << "/>\n";
|
stream << "/>\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1862,6 +1862,64 @@ void FacetsAnnotation::clear()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Following function takes data from a triangle and encodes it as string
|
||||||
|
// of hexadecimal numbers (one digit per triangle). Used for 3MF export,
|
||||||
|
// changing it may break backwards compatibility !!!!!
|
||||||
|
std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
|
||||||
|
{
|
||||||
|
std::string out;
|
||||||
|
|
||||||
|
auto triangle_it = m_data.find(triangle_idx);
|
||||||
|
if (triangle_it != m_data.end()) {
|
||||||
|
const std::vector<bool>& code = triangle_it->second;
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < int(code.size())) {
|
||||||
|
int next_code = 0;
|
||||||
|
for (int i=3; i>=0; --i) {
|
||||||
|
next_code = next_code << 1;
|
||||||
|
next_code |= int(code[offset + i]);
|
||||||
|
}
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
assert(next_code >=0 && next_code <= 15);
|
||||||
|
char digit = next_code < 10 ? next_code + '0' : (next_code-10)+'A';
|
||||||
|
out.insert(out.begin(), digit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Recover triangle splitting & state from string of hexadecimal values previously
|
||||||
|
// generated by get_triangle_as_string. Used to load from 3MF.
|
||||||
|
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str)
|
||||||
|
{
|
||||||
|
assert(! str.empty());
|
||||||
|
m_data[triangle_id] = std::vector<bool>(); // zero current state or create new
|
||||||
|
std::vector<bool>& code = m_data[triangle_id];
|
||||||
|
|
||||||
|
for (auto it = str.crbegin(); it != str.crend(); ++it) {
|
||||||
|
const char ch = *it;
|
||||||
|
int dec = 0;
|
||||||
|
if (ch >= '0' && ch<='9')
|
||||||
|
dec = int(ch - '0');
|
||||||
|
else if (ch >='A' && ch <= 'F')
|
||||||
|
dec = 10 + int(ch - 'A');
|
||||||
|
else
|
||||||
|
assert(false);
|
||||||
|
|
||||||
|
// Convert to binary and append into code.
|
||||||
|
for (int i=0; i<4; ++i) {
|
||||||
|
code.insert(code.end(), bool(dec & (1 << i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||||
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
||||||
bool model_object_list_equal(const Model &model_old, const Model &model_new)
|
bool model_object_list_equal(const Model &model_old, const Model &model_new)
|
||||||
|
|
|
@ -409,6 +409,8 @@ public:
|
||||||
bool set(const TriangleSelector& selector);
|
bool set(const TriangleSelector& selector);
|
||||||
indexed_triangle_set get_facets(const ModelVolume& mv, FacetSupportType type) const;
|
indexed_triangle_set get_facets(const ModelVolume& mv, FacetSupportType type) const;
|
||||||
void clear();
|
void clear();
|
||||||
|
std::string get_triangle_as_string(int i) const;
|
||||||
|
void set_triangle_from_string(int triangle_id, const std::string& str);
|
||||||
|
|
||||||
ClockType::time_point get_timestamp() const { return timestamp; }
|
ClockType::time_point get_timestamp() const { return timestamp; }
|
||||||
bool is_same_as(const FacetsAnnotation& other) const {
|
bool is_same_as(const FacetsAnnotation& other) const {
|
||||||
|
|
|
@ -603,6 +603,7 @@ void TriangleSelector::deserialize(const std::map<int, std::vector<bool>> data)
|
||||||
reset(); // dump any current state
|
reset(); // dump any current state
|
||||||
for (const auto& [triangle_id, code] : data) {
|
for (const auto& [triangle_id, code] : data) {
|
||||||
assert(triangle_id < int(m_triangles.size()));
|
assert(triangle_id < int(m_triangles.size()));
|
||||||
|
assert(! code.empty());
|
||||||
int processed_triangles = 0;
|
int processed_triangles = 0;
|
||||||
struct ProcessingInfo {
|
struct ProcessingInfo {
|
||||||
int facet_id = 0;
|
int facet_id = 0;
|
||||||
|
|
Loading…
Reference in a new issue