diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index cc2dbe6c3..8747324ff 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -325,7 +325,6 @@ struct ComparePolygonsWithProperties } -// @@@ void AsIfFlatRegion::merge_polygons_to (db::Shapes &output, bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const { db::EdgeProcessor ep (report_progress (), progress_desc ()); @@ -334,16 +333,101 @@ void AsIfFlatRegion::merge_polygons_to (db::Shapes &output, bool min_coherence, // count edges and reserve memory size_t n = 0; db::properties_id_type prop_id = 0; - bool need_split_props = false; + bool multiple_properties = false; for (RegionIterator s (begin ()); ! s.at_end (); ++s, ++n) { if (n == 0) { prop_id = s.prop_id (); - } else if (! need_split_props && prop_id != s.prop_id ()) { - need_split_props = true; + } else if (! multiple_properties && prop_id != s.prop_id ()) { + multiple_properties = true; } } - if (need_split_props) { + if (multiple_properties && join_properties_on_merge) { + + // this merge variant requires a two-step approach: we first and then join original properties IDs + // in a separate interaction step + + std::vector org_prop_ids; + org_prop_ids.reserve (n); + + n = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + size_t org_poly_id = 0; + for (RegionIterator p (begin ()); ! p.at_end (); ++p, ++org_poly_id) { + org_prop_ids.push_back (p.prop_id ()); + ep.insert (*p, org_poly_id); + } + + // and run the merge step + db::MergeOp op (min_wc); + db::PolygonContainer pc; + db::PolygonGenerator pg (pc, false /*don't resolve holes*/, min_coherence); + ep.process (pg, op); + + // reserve space for new (merged) polygons + for (auto p = pc.polygons ().begin (); p != pc.polygons ().end (); ++p) { + n += p->vertices (); + } + ep.reserve (n); + + size_t merged_poly_id = org_poly_id; + for (auto p = pc.polygons ().begin (); p != pc.polygons ().end (); ++p, ++merged_poly_id) { + ep.insert (*p, merged_poly_id); + } + + // compute interactions between merged and original polygons + + db::InteractionDetector id; + id.set_include_touching (false); + db::EdgeSink es; + ep.process (es, id); + id.finish (); + + // collect original property IDs per merged polygon + + std::vector > prop_ids_per_merged_polygon; + prop_ids_per_merged_polygon.resize (merged_poly_id - org_poly_id); + + for (auto ii = id.begin (); ii != id.end (); ++ii) { + auto first = std::min (ii->first, ii->second); + auto second = std::max (ii->first, ii->second); + if (first < org_poly_id && second >= org_poly_id) { + prop_ids_per_merged_polygon [second - org_poly_id].insert (org_prop_ids [first]); + } + } + + // Form new polygons with joined properties + + for (auto p = pc.polygons ().begin (); p != pc.polygons ().end (); ++p) { + + const std::set &prop_ids = prop_ids_per_merged_polygon [p - pc.polygons ().begin ()]; + + db::properties_id_type prop_id = 0; + if (prop_ids.size () == 1) { + prop_id = *prop_ids.begin (); + } else if (prop_ids.size () > 1) { + db::PropertiesSet ps; + for (auto p = prop_ids.begin (); p != prop_ids.end (); ++p) { + // merge in "larger one wins" mode - the advantage of this mode is that + // it is independent on the order of the attribute sets (which in fact are pointers) + ps.join_max (db::properties (*p)); + } + prop_id = db::properties_id (ps); + } + + if (prop_id != 0) { + output.insert (db::PolygonWithProperties (*p, prop_id)); + } else { + output.insert (*p); + } + + } + + } else if (multiple_properties) { db::Shapes result (output.is_editable ()); diff --git a/src/db/db/dbDeepRegion.cc b/src/db/db/dbDeepRegion.cc index 09ecc2bc4..8791b7ded 100644 --- a/src/db/db/dbDeepRegion.cc +++ b/src/db/db/dbDeepRegion.cc @@ -614,32 +614,11 @@ private: // join the properties db::PropertiesSet ps; for (auto a = attrs.begin (); a != attrs.end (); ++a) { - - if (skip_pseudo_label (*a)) { - - // skip attributes for pseudo-labels - - } else if (ps.empty ()) { - - ps = db::properties (*a); - - } else { - + if (! skip_pseudo_label (*a)) { // merge in "larger one wins" mode - the advantage of this mode is that // it is independent on the order of the attribute sets (which in fact are pointers) - const db::PropertiesSet &ps2 = db::properties (*a); - for (auto i = ps2.begin (); i != ps2.end (); ++i) { - auto f = ps.find (i->first); - if (f == ps.end ()) { - ps.insert_by_id (i->first, i->second); - } else if (db::property_value (f->second) < db::property_value (i->second)) { - ps.erase (i->first); - ps.insert_by_id (i->first, i->second); - } - } - + ps.join_max (db::properties (*a)); } - } s->second = db::properties_id (ps); diff --git a/src/db/db/dbPropertiesRepository.cc b/src/db/db/dbPropertiesRepository.cc index 7a526113b..8c7e17bc3 100644 --- a/src/db/db/dbPropertiesRepository.cc +++ b/src/db/db/dbPropertiesRepository.cc @@ -259,6 +259,32 @@ PropertiesSet::merge (const db::PropertiesSet &other) m_map.insert (other.m_map.begin (), other.m_map.end ()); } +void +PropertiesSet::join_max (const db::PropertiesSet &other) +{ + if (other.empty ()) { + + // ignore empty properties + + } else if (empty ()) { + + *this = other; + + } else { + + for (auto i = other.begin (); i != other.end (); ++i) { + auto f = find (i->first); + if (f == end ()) { + insert_by_id (i->first, i->second); + } else if (db::property_value (f->second) < db::property_value (i->second)) { + erase (i->first); + insert_by_id (i->first, i->second); + } + } + + } +} + std::multimap PropertiesSet::to_map () const { diff --git a/src/db/db/dbPropertiesRepository.h b/src/db/db/dbPropertiesRepository.h index 5596460b3..fc6e5af19 100644 --- a/src/db/db/dbPropertiesRepository.h +++ b/src/db/db/dbPropertiesRepository.h @@ -245,6 +245,11 @@ public: */ void merge (const db::PropertiesSet &other); + /** + * @brief Join another properties set with self, computing the maximum of both values in case of conflict + */ + void join_max (const db::PropertiesSet &other); + /** * @brief Gets the properties as a map */