Yes, I have dealt with similar issues. But I know of no simple set of rules that always work - there are too many possible factors entangling. I have usually struggled long enough that I think important situations are covered and then thrown in the towel on the rest. I have found it helpful to print out the calculated values of the new x, y, and z to understand what is going on.
The definition of “important situations” depends on the nature of the models you plan to annotate, so I won’t share code snippets that would risk mucking up a case that you consider to be important but I didn’t. For example, my Slope Markers extension expects that Edges of interest will be sloped and possibly not in any cardinal plane of the model, so it decides what to do based on the direction from which the camera is looking at the model. The logic got pretty complex and ad-hoc.
Note that these factors don’t depend on whether you use my vector technique to build the Transformation or build it by concatenating translate and rotate Transformations.
Among the factors are questions about which way the texts should read correctly. They are static objects once placed in 3D space, but you can orbit around the model to view them from different directions. For example, in your first image, the texts on the left face will be ok if viewed from the back side. The upside-down text at the bottom of the right face will be ok if you rotate the view so the model’s y axis points down on your screen, but then the one originally at the top will be upside-down. Et cetera.
Another confusing factor is that a Face may “use” an edge in the reverse direction from what its end - start would indicate. This happens because the outer loop of a Face always traces in counter-clockwise order around the Face’s normal (“right-hand rule”). But when two Faces share an Edge, they must use the Edge in opposite directions for this right-hand rule to be true for both of them. That’s why SketchUp has EdgeUse objects between Loops and Edges.
From basic vector algebra, if you determine two orthogonal axis vectors, you can always find the third using a cross product.
z_axis = x_axis.cross(y_axis)
x_axis = y_axis.cross(z_axis)
y_axis = z_axis.cross(x_axis)
So, they key is to determine which two will best constrain the orientation in a particular case.
For example, it is usually safe to assume that the new z axis should be parallel to the normal vector of the plane in which the labels are to be placed. This will make sure the front Faces of the 3DText will face upward when viewed looking toward the front of the Face.
It is usually also the case that the new x axis should be parallel to the associated Edge, as that axis is parallel to the baseline of the text. But that’s where it starts to get tricky because a parallel axis could be pointed in either of two directions. An Edge has a start point and an end point. You might assume that end - start would define a vector suitable for use as new_x. But depending on how the Edge was drawn and how it is being used by an adjacent Face, that might be backward from what you want! When the chosen new x axis is revesed, so will the new y axis calculated from it and z! That will cause the backward effect in some of your images.
When you are dealing with the outer loop of a Face, you can usually test whether y is how you want it by creating a test point offset in y by a small amount from the Edge midpoint and using Face#classify_point to check whether the test point is inside or outside the Face. If it is inside, you can conclude that you have the reversed case on the left in your first image. Once you have one Edge’s vector oriented correctly, the others will go around the Loop in the same direction.
For an inner loop, you need to check whether the Edge is also used by another Face. If so, ignore it on this Face and add the 3dText when you process the other Face’s outer loop. If the Edge isn’t used by any other Face, its loop will
The lower image is more of a problem because there is no Loop involved so you have to resort to some arbitrary notion of what is “right”. Cases such as the one on the lower right are hard to analyze, as there is no inherent notion of “outside” or “inside”.