How to order Vector3 arrays?

In Blender, I added cubes to a volume (a 3D letter or musical notes in this case, see screenshot). I managed to turn all these cubes into separate meshes, which I now import into Three via gLFT.

Next I extrude the positions of all those cubes and save them to the cubePositions array. I want to use this array to position a mesh from within (meaning: Blender and gLTF are “only” there to get that positions array).

// Save all the cubes' Vector3 position
const cubePositions = []
let delay = 100;

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

// Add the cubes successively one by one using a promise 
const delayedLoop = async() => {
    for (var i = 0; i < cubePositions.length; i++) {
        addCube( cubePositions[i] ) // function not shown in this script excerpt
        await sleep(delay); // wait a bit
    }
}

// extract positions of cube and save them to the `cubePositions` array
const gltfLoader = new GLTFLoader()
gltfLoader.load(
    '/models/en-3row-with-rotation-anim.gltf',
    (gltf) => {
        const mesh = gltf
        mesh.scene.children.forEach(cube => {
            cubePositions.push(cube.position)
        })
        // now order the array... but how?
        // ...?

        // Eventually add the cubes
        delayedLoop()
    },
)

Using an async/await approach I can add cubes to the scene in a staggered manner. This is, where I found out that the ordering of the cubes is rather arbitrary. Therefore I would like to order the Vector3 array. The order should be back-to-front (y-axis in Blender, z-axis in Three – if I am not mistaken) and left-to-right.

Unfortunately I have no idea how to do that…

How can arrays of Vector3 items be ordered that way?

you can just use the javascript sort function, no? cubePositions.sort((a, b) => a.z - b.z) or something like that.

Ah, yes, of course! Thank you so much for opening my eyes! (I thought Vector3 is something special and needs special operations… but it seems it is “just” Javascript :slight_smile: )

Follow-up question: Is there be a formula that would allow to sort Vector3 array according to 2 rules in one step? E.g. back-to-forth (z-index) and left-to-right?

(Currently I am doing that in 2 separate steps, which always return a new array.)

I was just about to write this, considering you have many of these boxes at the same say x value, you will want to probably sort them in the other two axis…

It’s still unfortunately just javascript.

const xMap = {}
points.forEach(p=>{
 if(xMap[p.x] === undefined) xMap[p.x] = []
 xMap[p.x].push(p)
})

And so on and so forth…

@dubois You are a gem! Thank you once more! I am very happy that it is still “just js” :slight_smile:

However, I will have to study your function and find out how it helps reducing two steps… at a glance it seems there are still two steps to sort by 2 Vector directions necessary.

I was pointed out that in C# they use “ThenBy”. So I searched if there was a js lib using that, and indeed I found this:

Haven’t looked at it yet, but am about to… in case you also check it out, I would love to read your comments about that lib!

If I’m not mistaken would you not be able to use

cubePositions.sort((a, b) => a.x - b.x || a.y - b.y || a.z - b.z);
 

This should sort first by X, next by Y and last by Z, also if any two x values are equal it would then sort that entry by y and if y values are equal would sort the entry by z…

const points: V3[] = [
  { x: 0, y: 0, z: 0 },
  { x: 0, y: 0, z: 1 },
  { x: 0, y: 0, z: 2 },

  { x: 1, y: 2, z: 2 },
  { x: 2, y: 2, z: 2 },
  { x: 2, y: 1, z: 2 },
  { x: 0, y: 2, z: 0 },
  { x: 0, y: 1, z: 1 },
  { x: 0, y: 1, z: 2 },

  { x: 0, y: 2, z: 2 },

  { x: 0, y: 2, z: 1 },
  { x: 1, y: 1, z: 0 },
  { x: 1, y: 0, z: 1 },
  { x: 2, y: 2, z: 0 },
  { x: 1, y: 0, z: 2 },

  { x: 1, y: 1, z: 2 },
  { x: 1, y: 1, z: 1 },

  { x: 1, y: 2, z: 0 },
  { x: 1, y: 2, z: 1 },

  { x: 2, y: 0, z: 1 },
  { x: 2, y: 0, z: 2 },
  { x: 2, y: 0, z: 0 },
  { x: 1, y: 0, z: 0 },

  { x: 2, y: 1, z: 0 },
  { x: 0, y: 1, z: 0 },

  { x: 2, y: 1, z: 1 },
  { x: 2, y: 2, z: 1 },
];


type V3 = { x: number; y: number; z: number };
const xMap: Record<number, Record<number, V3[]>> = {};
points.forEach((p) => {
  if (xMap[p.x] === undefined) xMap[p.x] = {};
  if (xMap[p.x][p.y] === undefined) xMap[p.x][p.y] = [];
  xMap[p.x][p.y].push(p);
});

const sortedX = Object.values(xMap).map((xGroup) => {
  const sortedY = Object.values(xGroup).map((yGroup) => {
    return yGroup.sort((a, b) => a.z - b.z);
  });
  return sortedY.sort(([a], [b]) => a.y - b.y);
});
sortedX.sort(([[a]], [[b]]) => a.x - b.x);

const res: V3[] = [];

sortedX.forEach((gx) => gx.forEach((gy) => gy.forEach((p) => res.push(p))));

console.log(res)

Or that lol, that is actually pretty good, and a one liner.

@Lawrence3DPK Thank you for your idea! That is a very nice solution!

The way your example is written sort the items

  • left-to-right
  • bottom-to-top
    -back-to-front

And depending on how I group the x, y and z operations, I can change that effect. Very smooth, very nice, very simple!

Thank you! :slight_smile:

@dubois Another solution - wow! Thank you so much!

I tried this one, too! The way you have written it yields exactely the same result as @Lawrence3DPK 's version. It is certainly quite a snippet to study and learn from!

But for the sake of simplcity I will stic with @Lawrence3DPK solution!

You guys are really very supportive here, and very quick in answering! That’s so motivating!
Highly appreciated!!!

Thank you again, @dubois and @Lawrence3DPK !