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.

4 Likes

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: )

1 Like

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…

1 Like

@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…

6 Likes
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)
2 Likes

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

2 Likes

@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:

1 Like

@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 !

1 Like