I assume the grid is regular ?
Parse the array into sub-sets of the same Z value.
Starting with the highest Z-set, find the max and min X and Ys in the set.
Test each point in an X column comparing to max Y and min Y. Add these to a points array.
Test each point in a Y row comparing to max X and min X. Add these to the points array.
Check to see that these points could form a face - if not do next lowest Z set...
Do next lowest Z set the same way... this time the face2 could also have an inner loop formed from face1 - add this points set into the array IF you want the 'hole'...
Do the next lowest Z set etc, face3 could have an inner loop formed by face2 outer loop etc...
Now we have a set of points that would form faces [facepts1.facepts2,facepts3,..] each with a perimeter that match the points Z height.
Assuming you want to move each face independently then make facegroup for each face - add_face(points) - and move it up in the Z.
In the group erase any face with only one outer loop IF it's NOT face1 - this forms the donut for face2/3/4 etc. If you want a face without a hole simply don't add the inner loop's points as above...
You can explode the groups if desired once the faces are placed.
Co-linear points that won't face, re-entrant loops and multiple holes etc might prove difficult ?
If the points are not 'gridded' a similar principle applies but you might want to [re]consider how pairs of max/min points are found...