From 77d35e8cc3efaf4060c726a07679077351776232 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 5 Sep 2024 09:54:48 +1000 Subject: [PATCH] pal cleanups - better variable names, comments --- src/core/pal/pal.cpp | 21 ++++----- src/core/pal/pal.h | 11 +++++ src/core/pal/problem.cpp | 96 ++++++++++++++++++++-------------------- src/core/pal/problem.h | 17 ++++--- 4 files changed, 81 insertions(+), 64 deletions(-) diff --git a/src/core/pal/pal.cpp b/src/core/pal/pal.cpp index 051de8e4d358..d53204dbc1ce 100644 --- a/src/core/pal/pal.cpp +++ b/src/core/pal/pal.cpp @@ -328,9 +328,9 @@ std::unique_ptr Pal::extractProblem( const QgsRectangle &extent, const prob->mFeatureCount = features.size(); prob->mTotalCandidates = 0; - prob->mFeatNbLp.resize( prob->mFeatureCount ); - prob->mFeatStartId.resize( prob->mFeatureCount ); - prob->mInactiveCost.resize( prob->mFeatureCount ); + prob->mCandidateCountForFeature.resize( prob->mFeatureCount ); + prob->mFirstCandidateIndexForFeature.resize( prob->mFeatureCount ); + prob->mUnlabeledCostForFeature.resize( prob->mFeatureCount ); if ( !features.empty() ) { @@ -396,17 +396,18 @@ std::unique_ptr Pal::extractProblem( const QgsRectangle &extent, const conflictProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Calculating conflicts" ), QStringLiteral( "rendering" ) ); } - int idlp = 0; - for ( std::size_t i = 0; i < prob->mFeatureCount; i++ ) /* for each feature into prob */ + int currentLabelPositionIndex = 0; + // loop through all the features registered in the problem + for ( std::size_t featureIndex = 0; featureIndex < prob->mFeatureCount; featureIndex++ ) { if ( feedback ) - feedback->setProgress( i * step ); + feedback->setProgress( featureIndex * step ); std::unique_ptr< Feats > feat = std::move( features.front() ); features.pop_front(); - prob->mFeatStartId[i] = idlp; - prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority ); + prob->mFirstCandidateIndexForFeature[featureIndex] = currentLabelPositionIndex; + prob->mUnlabeledCostForFeature[featureIndex] = std::pow( 2, 10 - 10 * feat->priority ); std::size_t maxCandidates = 0; switch ( feat->feature->getGeosType() ) @@ -520,14 +521,14 @@ std::unique_ptr Pal::extractProblem( const QgsRectangle &extent, const return nullptr; // update problem's # candidate - prob->mFeatNbLp[i] = static_cast< int >( feat->candidates.size() ); + prob->mCandidateCountForFeature[featureIndex] = static_cast< int >( feat->candidates.size() ); prob->mTotalCandidates += static_cast< int >( feat->candidates.size() ); // add all candidates into a rtree (to speed up conflicts searching) for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates ) { candidate->insertIntoIndex( prob->allCandidatesIndex() ); - candidate->setProblemIds( static_cast< int >( i ), idlp++ ); + candidate->setProblemIds( static_cast< int >( featureIndex ), currentLabelPositionIndex++ ); } features.emplace_back( std::move( feat ) ); } diff --git a/src/core/pal/pal.h b/src/core/pal/pal.h index 56e2ab12ed47..149f25f6ee04 100644 --- a/src/core/pal/pal.h +++ b/src/core/pal/pal.h @@ -133,6 +133,17 @@ namespace pal * boundary, which will be used to detect whether a label is visible (or partially visible) in * the rendered map. This may differ from \a extent in the case of rotated or non-rectangular * maps. + * + * This method: + * + * - preprocesses features, eg merging connected lines, chopping features at repeat distances + * - creates label candidates for every feature + * - purges candidates outside the map extent (respecting whether partial labels should be shown at the map boundary) + * - creates default fallback candidates for features with no valid candidates + * - collects obstacles + * - calculates candidate costs + * - calculates overlaps/conflicts + * - eliminates hard conflicts (forbidden placement) */ std::unique_ptr< Problem > extractProblem( const QgsRectangle &extent, const QgsGeometry &mapBoundary, QgsRenderContext &context ); diff --git a/src/core/pal/problem.cpp b/src/core/pal/problem.cpp index 55df4fdcb094..46ec3a916308 100644 --- a/src/core/pal/problem.cpp +++ b/src/core/pal/problem.cpp @@ -70,53 +70,47 @@ Problem::~Problem() = default; void Problem::reduce() { - int i; - int j; int k; int counter = 0; int lpid; - bool *ok = new bool[mTotalCandidates]; - bool run = true; - - for ( i = 0; i < mTotalCandidates; i++ ) - ok[i] = false; - + std::vector ok( mTotalCandidates, false ); double amin[2]; double amax[2]; LabelPosition *lp2 = nullptr; - while ( run ) + while ( true ) { if ( pal->isCanceled() ) break; - run = false; - for ( i = 0; i < static_cast< int >( mFeatureCount ); i++ ) + bool finished = true; + for ( std::size_t feature = 0; feature < mFeatureCount; feature++ ) { if ( pal->isCanceled() ) break; - // ok[i] = true; - for ( j = 0; j < mFeatNbLp[i]; j++ ) // for each candidate + // for each candidate + const int totalCandidatesForFeature = mCandidateCountForFeature[feature]; + for ( int candidateIndex = 0; candidateIndex < totalCandidatesForFeature; candidateIndex++ ) { - if ( !ok[mFeatStartId[i] + j] ) + if ( !ok[mFirstCandidateIndexForFeature[feature] + candidateIndex] ) { - if ( mLabelPositions.at( mFeatStartId[i] + j )->getNumOverlaps() == 0 ) // if candidate has no overlap + if ( mLabelPositions.at( mFirstCandidateIndexForFeature[feature] + candidateIndex )->getNumOverlaps() == 0 ) // if candidate has no overlap { - run = true; - ok[mFeatStartId[i] + j] = true; + finished = false; + ok[mFirstCandidateIndexForFeature[feature] + candidateIndex] = true; // 1) remove worse candidates from candidates // 2) update nb_overlaps - counter += mFeatNbLp[i] - j - 1; + counter += totalCandidatesForFeature - candidateIndex - 1; - for ( k = j + 1; k < mFeatNbLp[i]; k++ ) + for ( k = candidateIndex + 1; k < totalCandidatesForFeature; k++ ) { - lpid = mFeatStartId[i] + k; + lpid = mFirstCandidateIndexForFeature[feature] + k; ok[lpid] = true; lp2 = mLabelPositions[lpid ].get(); @@ -136,16 +130,18 @@ void Problem::reduce() lp2->removeFromIndex( mAllCandidatesIndex ); } - mFeatNbLp[i] = j + 1; + mCandidateCountForFeature[feature] = candidateIndex + 1; break; } } } } + + if ( finished ) + break; } this->mTotalCandidates -= counter; - delete[] ok; } void Problem::ignoreLabel( const LabelPosition *lp, PriorityQueue &list, PalRtree< LabelPosition > &candidatesIndex ) @@ -184,10 +180,12 @@ void Problem::init_sol_falp() LabelPosition *lp = nullptr; - for ( int i = 0; i < static_cast< int >( mFeatureCount ); i++ ) - for ( int j = 0; j < mFeatNbLp[i]; j++ ) + for ( int feature = 0; feature < static_cast< int >( mFeatureCount ); feature++ ) + { + const int totalCandidatesForFeature = mCandidateCountForFeature[feature]; + for ( int candidateIndex = 0; candidateIndex < totalCandidatesForFeature; candidateIndex++ ) { - label = mFeatStartId[i] + j; + label = mFirstCandidateIndexForFeature[feature] + candidateIndex; try { list.insert( label, mLabelPositions.at( label )->getNumOverlaps() ); @@ -197,6 +195,7 @@ void Problem::init_sol_falp() continue; } } + } while ( list.getSize() > 0 ) // O (log size) { @@ -217,9 +216,9 @@ void Problem::init_sol_falp() const int probFeatId = lp->getProblemFeatureId(); mSol.activeLabelIds[probFeatId] = label; - for ( int i = mFeatStartId[probFeatId]; i < mFeatStartId[probFeatId] + mFeatNbLp[probFeatId]; i++ ) + for ( int candidateIndex = mFirstCandidateIndexForFeature[probFeatId]; candidateIndex < mFirstCandidateIndexForFeature[probFeatId] + mCandidateCountForFeature[probFeatId]; candidateIndex++ ) { - ignoreLabel( mLabelPositions[ i ].get(), list, mAllCandidatesIndex ); + ignoreLabel( mLabelPositions[ candidateIndex ].get(), list, mAllCandidatesIndex ); } @@ -245,20 +244,18 @@ void Problem::init_sol_falp() if ( mDisplayAll ) { - int nbOverlap; - int start_p; LabelPosition *retainedLabel = nullptr; - int p; for ( std::size_t i = 0; i < mFeatureCount; i++ ) // forearch hidden feature { if ( mSol.activeLabelIds[i] == -1 ) { - nbOverlap = std::numeric_limits::max(); - start_p = mFeatStartId[i]; - for ( p = 0; p < mFeatNbLp[i]; p++ ) + int nbOverlap = std::numeric_limits::max(); + const int firstCandidateIdForFeature = mFirstCandidateIndexForFeature[i]; + const int totalCandidatesForFeature = mCandidateCountForFeature[i]; + for ( int candidateIndexForFeature = 0; candidateIndexForFeature < totalCandidatesForFeature; candidateIndexForFeature++ ) { - lp = mLabelPositions[ start_p + p ].get(); + lp = mLabelPositions[ firstCandidateIdForFeature + candidateIndexForFeature ].get(); lp->resetNumOverlaps(); lp->getBoundingBox( amin, amax ); @@ -308,8 +305,6 @@ inline Chain *Problem::chain( int seed ) const int max_degree = pal->mEjChainDeg; - int seedNbLp; - QLinkedList currentChain; QLinkedList conflicts; @@ -320,10 +315,12 @@ inline Chain *Problem::chain( int seed ) double amin[2]; double amax[2]; + // delta is actually related to the cost? delta = 0; + // seed is actually the feature number! while ( seed != -1 ) { - seedNbLp = mFeatNbLp[seed]; + const int totalCandidatesForThisFeature = mCandidateCountForFeature[seed]; delta_min = std::numeric_limits::max(); next_seed = -1; @@ -331,20 +328,20 @@ inline Chain *Problem::chain( int seed ) // sol[seed] is ejected if ( tmpsol[seed] == -1 ) - delta -= mInactiveCost[seed]; + delta -= mUnlabeledCostForFeature[seed]; else delta -= mLabelPositions.at( tmpsol[seed] )->cost(); - for ( int i = -1; i < seedNbLp; i++ ) + for ( int i = -1; i < totalCandidatesForThisFeature ; i++ ) { try { // Skip active label ! - if ( !( tmpsol[seed] == -1 && i == -1 ) && i + mFeatStartId[seed] != tmpsol[seed] ) + if ( !( tmpsol[seed] == -1 && i == -1 ) && i + mFirstCandidateIndexForFeature[seed] != tmpsol[seed] ) { if ( i != -1 ) // new_label { - lid = mFeatStartId[seed] + i; + lid = mFirstCandidateIndexForFeature[seed] + i; delta_tmp = delta; lp = mLabelPositions[ lid ].get(); @@ -371,7 +368,7 @@ inline Chain *Problem::chain( int seed ) if ( !conflicts.contains( feat ) ) { conflicts.append( feat ); - delta_tmp += lp2->cost() + mInactiveCost[feat]; + delta_tmp += lp2->cost() + mUnlabeledCostForFeature[feat]; } } return true; @@ -461,7 +458,7 @@ inline Chain *Problem::chain( int seed ) const int ftid = conflicts.takeFirst(); newChain->feat[j] = ftid; newChain->label[j] = -1; - newChain->delta += mInactiveCost[ftid]; + newChain->delta += mUnlabeledCostForFeature[ftid]; j++; } @@ -482,7 +479,7 @@ inline Chain *Problem::chain( int seed ) } else // Current label == -1 end of chain ... { - if ( !retainedChain || delta + mInactiveCost[seed] < delta_best ) + if ( !retainedChain || delta + mUnlabeledCostForFeature[seed] < delta_best ) { if ( retainedChain ) { @@ -492,7 +489,7 @@ inline Chain *Problem::chain( int seed ) else retainedChain = new Chain(); - delta_best = delta + mInactiveCost[seed]; + delta_best = delta + mUnlabeledCostForFeature[seed]; retainedChain->degree = currentChain.size() + 1; retainedChain->feat = new int[retainedChain->degree]; @@ -510,7 +507,7 @@ inline Chain *Problem::chain( int seed ) } retainedChain->feat[j] = seed; retainedChain->label[j] = -1; - retainedChain->delta = delta + mInactiveCost[seed]; + retainedChain->delta = delta + mUnlabeledCostForFeature[seed]; } } } @@ -581,7 +578,6 @@ void Problem::chainSearch( QgsRenderContext & ) return; int i; - int seed; bool *ok = new bool[mFeatureCount]; int fid; int lid; @@ -597,6 +593,8 @@ void Problem::chainSearch( QgsRenderContext & ) double amin[2]; double amax[2]; + // seed is actually the feature ID, maybe should be renamed? + int seed; while ( true ) { for ( seed = ( iter + 1 ) % mFeatureCount; @@ -669,7 +667,7 @@ QList Problem::getSolution( bool returnInactive, QList( mLabelPositions.size() ); if ( foundNonOverlappingPlacement ) @@ -686,7 +684,7 @@ QList Problem::getSolution( bool returnInactive, QListpush_back( mLabelPositions[ startIndexForLabelPlacements ].get() ); } } diff --git a/src/core/pal/problem.h b/src/core/pal/problem.h index 89f026367289..d6aa103d00b4 100644 --- a/src/core/pal/problem.h +++ b/src/core/pal/problem.h @@ -103,13 +103,16 @@ namespace pal /** * Returns the number of candidates generated for the \a feature at the specified index. */ - int featureCandidateCount( int feature ) const { return mFeatNbLp[feature]; } + int featureCandidateCount( int feature ) const { return mCandidateCountForFeature[feature]; } /** * Returns the candidate corresponding to the specified \a feature and \a candidate index. */ - LabelPosition *featureCandidate( int feature, int candidate ) const { return mLabelPositions[ mFeatStartId[feature] + candidate ].get(); } + LabelPosition *featureCandidate( int feature, int candidate ) const { return mLabelPositions[ mFirstCandidateIndexForFeature[feature] + candidate ].get(); } + /** + * Gets called AFTER extractProblem. + */ void reduce(); /** @@ -201,9 +204,12 @@ namespace pal std::vector< std::unique_ptr< LabelPosition > > mPositionsWithNoCandidates; - std::vector< int > mFeatStartId; - std::vector< int > mFeatNbLp; - std::vector< double > mInactiveCost; + //! Index of the position in mLabelPositions which corresponds to the first candidate for a feature, array index corresponds to label feature index + std::vector< int > mFirstCandidateIndexForFeature; + //! Total number of registered candidates for each feature, array index corresponds to label feature index + std::vector< int > mCandidateCountForFeature; + //! Cost for excluding (ie not labeling) a feature, array index corresponds to label feature index + std::vector< double > mUnlabeledCostForFeature; class Sol { @@ -221,6 +227,7 @@ namespace pal Sol mSol; double mNbOverlap = 0.0; + // seed is actually a feature ID, maybe it should be renamed? Chain *chain( int seed ); Pal *pal = nullptr;