How to use std::sort to loop through one variable within a struct and rearrange another?

136 views Asked by At

I'm honestly at a loss as to why my question is being called a duplicate as all the other examples show how to sort a variable within a struct. I am trying to sort a variable within a struct based on another, and this I cannot find examples of. Below is my question in the hopes someone can help:

I have a rudimentary particle system I've developed for learning.

My main particle is contained in a struct which looks like this:

struct Particle {
float px;  // x-position
float py;  // y-position
float pz;  // z-position
float scale;
float rotation;
float red;
float green;
float blue;
float alpha;
int index; };

Index is the particle ID. As I create each new particle, it gets saved into the Particle vector array called std::vector<Particle> particles; which I increment by one. Finally, I draw them after that.

I now want to introduce a pass for depth sorting, so that I can draw the particles from furthest to closest.

I understand that std::sort will work. My understanding is that I need to loop through the particles.pz (z position) but apply that sorting result to particles.index. So the index will ultimately be rearranged and hold the correct drawing order. I have this working already but not using std::sort and it is painfully slow. Thus, I'm hoping std::sort will provide a boost in performance.

I see that to do that, you can sort through the struct like this:

    std::sort(particles.begin(), particles.end(), [](const auto& lhs, const auto& rhs)
        {
            return lhs.pz1 > rhs.pz1;
        });

But this will rearrange the z positions whereas I want to rearrange the particle index BASED on the z position sort. I've looked at many other examples but none seem to address this.

I am not so experienced with structs, let alone sorting, so obviously I am doing something wrong.

How can I modify this code so that it sorts through the particle z positions from furthest to closest, and applies that sorting to the particle index?

ADDENDUM:

Perhaps this will help. This code shows my current sorting algorithm and how it works. I am trying to optimize this and see if std::sort can perform better. This is before I was using my struct so the variables are named differently and I hope I copied it correctly but it is mainly for illustrative purposes. (Counter is total particles).

            for (int i = 0; i < counter - 1; ++i) {
            for (int j = 0; j < counter - 1; ++j) {
                int p = particles[j].index;
                int q = particles[j + 1].index;

                if ( particles[p].pz > particles[q].pz) {
                    unsigned int tmp = particles[j].index;
                    particles[j].index = particles[j + 1].index;
                    particles[j + 1].index = tmp;

                }
            }
        }

CONCLUSION:

@PaulMcKenzie was able to help me understand and solve this. He went the extra mile to write out example code which I will post below as my accepted answer. After having to do some minor adjustments, I can confirm his solution works and it is much faster. Whereas my tests came in at 16 seconds of computation his solution using std::sort brought it down to 3.8 seconds. Thanks again!

#include <algorithm>
#include <vector>
#include <numeric>
#include <iostream>
#include <random>

struct Particle {
float px;  // x-position
float py;  // y-position
float pz;  // z-position
float scale;
float rotation;
float red;
float green;
float blue;
float alpha;
};

int main()
{
    // random number generator
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<float> zValues(1.0f, 1000.0f);
   
    // Fill vector with random z values
    std::vector<Particle> vParticles(10);
    for (auto& v : vParticles )
       v.pz = zValues(gen);

    std::vector<int> index(10);
    std::cout << "Original order by z:\n";
    for (auto& v : vParticles)        
      std::cout << v.pz << "\n";

    // set up index array
    std::iota(index.begin(), index.end(), 0);

    // Sort via the index
    std::sort(index.begin(), index.end(), [&](int n1, int n2) 
        { return vParticles[n1].pz < vParticles[n2].pz;});

    std::cout << "\nSorted order by z:\n";
    for (auto& i : index)        
      std::cout << vParticles[i].pz << " " << " is at index " << i << "\n";

    std::cout << "\nHere is the final index array:\n";
    for (auto& i : index)        
        std::cout << i << "\n";

    std::cout << "\nProof that vector has not changed:\n";
    for (auto& v : vParticles)        
      std::cout << v.pz << "\n";
}
1

There are 1 answers

0
Rich95 On

@PaulMcKenzie was able to help me understand and solve this. He went the extra mile to write out example code which I will post below as my accepted answer. After having to do some minor adjustments, I can confirm his solution works and it is much faster. Whereas my tests came in at 16 seconds of computation his solution using std::sort brought it down to 3.8 seconds. Thanks again!

#include <algorithm>
#include <vector>
#include <numeric>
#include <iostream>
#include <random>

struct Particle {
float px;  // x-position
float py;  // y-position
float pz;  // z-position
float scale;
float rotation;
float red;
float green;
float blue;
float alpha;
};

int main()
{
    // random number generator
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<float> zValues(1.0f, 1000.0f);
   
    // Fill vector with random z values
    std::vector<Particle> vParticles(10);
    for (auto& v : vParticles )
       v.pz = zValues(gen);

    std::vector<int> index(10);
    std::cout << "Original order by z:\n";
    for (auto& v : vParticles)        
      std::cout << v.pz << "\n";

    // set up index array
    std::iota(index.begin(), index.end(), 0);

    // Sort via the index
    std::sort(index.begin(), index.end(), [&](int n1, int n2) 
        { return vParticles[n1].pz < vParticles[n2].pz;});

    std::cout << "\nSorted order by z:\n";
    for (auto& i : index)        
      std::cout << vParticles[i].pz << " " << " is at index " << i << "\n";

    std::cout << "\nHere is the final index array:\n";
    for (auto& i : index)        
        std::cout << i << "\n";

    std::cout << "\nProof that vector has not changed:\n";
    for (auto& v : vParticles)        
      std::cout << v.pz << "\n";
}