Compare commits

..
Author SHA1 Message Date
ExPikaPaka 991d421756 Stop reading a missing AMF metadata type as a string
A <metadata> element without a type attribute makes get_attribute() return
nullptr, which is then assigned to a std::string and read as a C string.

Check it the way the sibling metadata handler already does and stop the parse.
2026-10-01 08:50:05 +02:00
3 changed files with 27 additions and 21 deletions
+3 -1
View File
@@ -304,7 +304,8 @@ ConfigOption* ConfigOptionDef::create_default_option() const
return new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()); return new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt());
if (type == coEnums) { if (type == coEnums) {
if (this->default_value->nullable()) { auto dft = this->default_value->clone();
if (dft->nullable()) {
ConfigOptionEnumsGenericNullable *opt = dynamic_cast<ConfigOptionEnumsGenericNullable *>(this->default_value->clone()); ConfigOptionEnumsGenericNullable *opt = dynamic_cast<ConfigOptionEnumsGenericNullable *>(this->default_value->clone());
opt->keys_map = this->enum_keys_map; opt->keys_map = this->enum_keys_map;
return opt; return opt;
@@ -313,6 +314,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const
opt->keys_map = this->enum_keys_map; opt->keys_map = this->enum_keys_map;
return opt; return opt;
} }
delete dft;
} }
return this->default_value->clone(); return this->default_value->clone();
+6 -1
View File
@@ -311,9 +311,14 @@ void AMFParserContext::startElement(const char *name, const char **atts)
case 2: case 2:
if (strcmp(name, "metadata") == 0) { if (strcmp(name, "metadata") == 0) {
if (m_path[1] == NODE_TYPE_MATERIAL || m_path[1] == NODE_TYPE_OBJECT) { if (m_path[1] == NODE_TYPE_MATERIAL || m_path[1] == NODE_TYPE_OBJECT) {
m_value[0] = get_attribute(atts, "type"); const char *type = get_attribute(atts, "type");
if (type == nullptr)
this->stop();
else {
m_value[0] = type;
node_type_new = NODE_TYPE_METADATA; node_type_new = NODE_TYPE_METADATA;
} }
}
}/* else if (strcmp(name, "layer_config_ranges") == 0 && m_path[1] == NODE_TYPE_OBJECT) }/* else if (strcmp(name, "layer_config_ranges") == 0 && m_path[1] == NODE_TYPE_OBJECT)
node_type_new = NODE_TYPE_LAYER_CONFIG;*/ node_type_new = NODE_TYPE_LAYER_CONFIG;*/
else if (strcmp(name, "mesh") == 0) { else if (strcmp(name, "mesh") == 0) {
+11 -12
View File
@@ -2087,6 +2087,11 @@ void ModelVolume::reset_extra_facets()
this->seam_facets.reset(); this->seam_facets.reset();
this->mmu_segmentation_facets.reset(); this->mmu_segmentation_facets.reset();
this->fuzzy_skin_facets.reset(); this->fuzzy_skin_facets.reset();
// Texture-displacement paint data has no remap-across-topology-change support yet (see
// build_texture_displacement()'s documented limitation), so it must be dropped here rather
// than left referring to a mesh that no longer matches it.
for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i)
this->texture_displacement_facet(i).reset();
} }
std::optional<TriangleSelector::SavedPainting> ModelVolume::save_painting() const std::optional<TriangleSelector::SavedPainting> ModelVolume::save_painting() const
@@ -2648,14 +2653,9 @@ std::vector<int> ModelVolume::get_extruders() const
return std::vector<int>(); return std::vector<int>();
if (mmu_segmentation_facets.timestamp() != mmuseg_ts) { if (mmu_segmentation_facets.timestamp() != mmuseg_ts) {
std::vector<indexed_triangle_set> its_per_type;
mmuseg_extruders.clear(); mmuseg_extruders.clear();
mmuseg_ts = mmu_segmentation_facets.timestamp(); mmuseg_ts = mmu_segmentation_facets.timestamp();
// ORCA: without painting data every facet keeps its default (NONE) state, so no extruder
// other than the volume's own one can be painted on it. Skip get_facets() then: it builds a
// TriangleSelector with one node per facet over the whole mesh (tens of MiB on a dense one)
// only to hand back empty sets for every extruder.
if (! mmu_segmentation_facets.empty()) {
std::vector<indexed_triangle_set> its_per_type;
mmu_segmentation_facets.get_facets(*this, its_per_type); mmu_segmentation_facets.get_facets(*this, its_per_type);
for (int idx = 1; idx < its_per_type.size(); idx++) { for (int idx = 1; idx < its_per_type.size(); idx++) {
indexed_triangle_set& its = its_per_type[idx]; indexed_triangle_set& its = its_per_type[idx];
@@ -2665,7 +2665,6 @@ std::vector<int> ModelVolume::get_extruders() const
mmuseg_extruders.push_back(idx); mmuseg_extruders.push_back(idx);
} }
} }
}
std::vector<int> volume_extruders = mmuseg_extruders; std::vector<int> volume_extruders = mmuseg_extruders;
@@ -2995,6 +2994,11 @@ void ModelVolume::assign_new_unique_ids_recursive()
seam_facets.set_new_unique_id(); seam_facets.set_new_unique_id();
mmu_segmentation_facets.set_new_unique_id(); mmu_segmentation_facets.set_new_unique_id();
fuzzy_skin_facets.set_new_unique_id(); fuzzy_skin_facets.set_new_unique_id();
// As set_new_unique_id() already does: the undo/redo stack stores FacetsAnnotation contents keyed
// by ObjectID, so a clone left sharing these ids with its source can be handed the source's mask
// on an undo - after which a paint mask and the mesh it was recorded against no longer match.
for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i)
texture_displacement_facet(i).set_new_unique_id();
} }
void ModelVolume::rotate(double angle, Axis axis) void ModelVolume::rotate(double angle, Axis axis)
@@ -3632,11 +3636,6 @@ ModelInstanceEPrintVolumeState ModelInstance::calc_print_volume_state(const Buil
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
{ {
// ORCA: nothing is painted, so only the NONE state can have facets. Answering for any other
// state needs no TriangleSelector (one node per facet of the whole mesh) at all.
if (this->empty() && type != EnforcerBlockerType::NONE)
return {};
TriangleSelector selector(mv.mesh()); TriangleSelector selector(mv.mesh());
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize(). // Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
selector.deserialize(m_data, false); selector.deserialize(m_data, false);