Hello.
Is there a way to detect if the user has clicked Reverse Face?
I have 2 different materials for both front and back for each Face and i need to have the same material on both.
I tried doing that, but on Reverse Face is flipping the material.
front:
front after Reverse Faces:
I didn’t found anything relevant on Ruby API documentation.
It is not recorded whether the user has clicked Reverse Face from the context menu. However you can check the orientation of a face using Face#normal. You can compare this vector to some sort of reference vector (using dot multiplication, %) to see if the face is reversed or not. The problem is how to define the reference vector. What is your use case?
It doesn’t take too much thinking to realize that face orientation is view dependent. If, as often seems to happen with glass for windows, parts of the model aren’t made 3D, face orientation can only be correct when viewing from one side but not the other.
If you are inside a solid looking out then you are probably looking at the back side of the face. Using face.material=‘some_texture’ is always going to put it on the front face regardless of which ‘side’ you picked.
Therein is a crucial aspect to answering the question: How have you determined that the user clicked a Face?
For example, is this test within one of the Tool’s button callbacks such as onLButtonDown and you used a PickHelper to identify the Face? Or are you finding the Face within the current selection? Or by some other means?
It matters because if you got the Face from PickHelper in a Tool callback, you can assume that the user picked the currently visible side, which is what @sdmitch’s code tests regardless of where the camera is located. If you are getting the Face from the Selection, you don’t know whether the user may have moved the camera after making the pick, so there is a chance of error.
The OP seems to refer to the native context menu’s Reverse Faces command.
I just tested it for a change in native tool, and a got not a peep out of a ToolsObserver.
Whoops ...
Likewise a test on EntitiesObserver and EntityObserver does not fire for the Reverse Faces or Orient Faces context menu commands.
EDIT: Whoops test was incorrect.
Today I tested again, and both Reverse Faces and Orient Faces context commands DO actually fire EntityObserver’s #onChangeEntity callback method.
ADD: Also success in testing an API face.reverse! call. It also fires the #onChangeEntity callback.
For solid or pseudo-solid, that is, shape where there some kind of inside and outside, there is a way to determine the right orientation of faces independently of the view camera. Solid or pseudo solid are shapes where all edges in the shape have only one or two bordering faces.
The algorithm is greedy, unless you have a starting face which you know is correctly oriented (in which case it is quite fast to expand the right orientation to all neighboring faces progressively). I use this approach in the plugin FredoTools::AutoReverseFaces
Hmm…on re-reading the original post, I see your point but I’m confused what the OP is really trying to accomplish.
Unless a material has already been applied to both the front and back of a face, the un-altered side will still have the default material and that is what will show if the user clicks Reverse Faces, just as it would if you orbit to view the back side.
But perhaps the problem is due to a strange glitch (or maybe bug) in how faces handle UV coordinates for texture images mapped to faces. At least for vertical faces, that is, ones that contain the z axis, the image coordinates are left-right backward on the back side of the face! This causes a texture image applied to the back side to be left-right reversed, as the OP’s images illustrate. So, if you apply a texture image to both the front and back sides of a face, it will be reversed on the back side regardless of how you come to view that side - Reverse Faces just changes where the camera must be to see the flipped image. To avoid this you need a pre-reversed version of the texture to use for back sides. Which brings us back to the question of when and how the texture was applied to the face.
Edit: on some further testing, it appears that this is true for any face, not just vertical.
If the object is a solid (aka closed mesh) it is possible to extend the algorithm to not require a starting face with a correct orientation.
The volume of a closed oriented mesh can be calculated by iterating all its triangles. For each triangle the volume of a tetrahedron formed between the triangle and an arbitrary point is calculated. The point can in theory be anywhere but having it close to the other points, e.g. in the bounding box center lowers the floating point precision problems. If the arbitrary point is in the front of the face the volume is regarded negative and if the point is at the back of the face the volume is regarded positive. To know what side of the triangle’s plane the point is on the vector between the point and its projection onto the triangle’s plane can be compared to the triangle’s normal using dot multiplication. The volume of all tetrahedrons are summed up. If the volume is negative the mesh is oriented inwards; if positive the mesh is oriented outwards.
If an arbitrary face is first picked and all other faces are oriented to match it, the above algorithm can be used to determine if this arbitrary face was wrongly oriented, and all faces can be reversed in such case.
If you are given a face with the right orientation, it is straightforward to extend the orientation to all neighbor faces, regardless of whether the shape is a solid or not (assuming however that you do not meet edges connected to 3 or more faces)
If the shape is a true solid, you need one single test (nb intersections) to determine the right orientation of an arbitrary face of the shape, and then you are back to case 1.
if the shape is an open solid, then it is still possible to determine what would be the inside and the outside, but this requires a test between more faces, and the algorithm is more greedy in computation.
Well…if you place a texture with a black disk (simulating a hole) which is on the right side on the front face, then Sketchup will make it appear on the left side on the reverse face.
So somehow, if you could see the front and the back face at the same time, you see the two black disk exactly in the same location, simulating a kind of hole.
This is also the case if you apply a transparent texture simulating a fence, or a brick texture, which would give you the impression that, on the back face, you see the back of the bricks as they are laid out on the front face.
Now, I agree. If the texture includes readable text, this does not really work for reading…
…but it seems to be natural that on the back face you the see the back of what you see on the front face (even if you did not put it yet on the front face!).
Today I tested again, and both Reverse Faces and Orient Faces context commands DO actually fire EntityObserver’s #onChangeEntity callback method.
ADD: Also success in testing an API face.reverse! call. It also fires the #onChangeEntity callback.
Not sure what I did wrong in the first test. (Likely copied the class example and thought the example callback was #onChangeEntity instead of #onEraseEntity.)
I think the text example is good one to exhibit “normal” behavior. If one were to put a vinyl text sign on a store window, it would look correct from the exterior but backwards from inside and that would be “normal” expected behavior. The sketchup reverse face respects this. To cheat that I imported a reversed (horizontally flipped) version of the texture onto the reverse face knowing SU would flip it so that it would appear normal.
As we can’t see both sides at the same time, this deception may be unnoticeable, but in the case of the store window, if we were inside it might look odd that the text is reading correctly from the inside?
In Robert’s case, cheating this seems to be the fix?
Alternatively, and this is also a real world condition, glass or any other surface has some thickness and so does have two independent faces that are not shared/common and so applying the same texture would read correctly from either side. So in the case of the store window, if the glass pane were modeled “correctly” and had thickness you would have to add a reversed logo/texture to the inside face to read as expected.