Weird face normal behaviour in rotated component
-
I first came across this when trying to write a Sketchup Tool, and posted recently in the Plugins forum (http://sketchucation.com/forums/viewtopic.php?f=323%26amp;t=60165).
I now wonder if this is a bug in Sketchup, as I can repeat it by drawing manually.
Steps to reproduce in SU:
-
Draw a rectangle at the origin (say 2" x 1"). Make it into a component called (for example) Face.
-
Rotate it 30 degrees about the Z-axis on its lower left corner.
-
Rotate it 30 degrees down out of the x-y plane, rotating about its short side.
-
Open the component for editing, and use PushPull tool to extrude (say) 4". Close the component again.
-
Use Chris Fullmer's plugin Vertical Line Tools/Line on Normal to draw a normal to the component face.
Instead of actually BEING normal to the face, it is parallel to the Y axis.
If I open the component for editing, copy the front face, close the component, then paste the face into the drawing, it is drawn in the x-z plane, where its normal IS parallel to the Y-axis.
And the bounding box is correctly tight round the component (highlighted in blue), so its axes are correctly along its edges.
Chris's tool won't actually draw a normal to the face inside the component, reporting a divide by zero error.
I had what appears to be exactly the same problem in Ruby - face.normal on my component is NOT normal to the component face.
If this isn't a bug in the face.normal method, why does it happen by design? And how can I find a workaround? I had thought I was doing something logically wrong in my Ruby code, but couldn't find it. Now I don't know what to do, if this isn't a bug.
PS. The plugin behaves just the same in SU2013 Make. So maybe I'm just misunderstanding what OUGHT to happen when you draw a face normal. It's certainly counter-intuitive.
-
-
it may be that chris is using the similar code to you...
have you looked at how fredo does it with http://sketchucation.com/forums/viewtopic.php?p=330985#p330985
john
-
Thanks for the pointer. I'll look in a day or two - too late tonight/this morning, it's Christmas Day.
I know I'm just using the Face.normal method on a picked face which is in a (closed) component. I guess Chris probably is too.
Oddly, I don't get the same behaviour when the face is in a group - his tool works as I'd expect on a face that's in a group.
I could see how to correct for this, but can't quite work out how to tell if the face is 'loose geometry', in a group, or in a component. I'll get there soon, I think, with some combination of
if face.parent.is_a? Sketchup::ComponentDefinition, and if not face.parent.group?, and error trapping -
@johnwmcc said:
Thanks for the pointer. I'll look in a day or two - too late tonight/this morning, it's Christmas Day.
I know I'm just using the Face.normal method on a picked face which is in a (closed) component. I guess Chris probably is too.
Oddly, I don't get the same behaviour when the face is in a group - his tool works as I'd expect on a face that's in a group.
I could see how to correct for this, but can't quite work out how to tell if the face is 'loose geometry', in a group, or in a component. I'll get there soon, I think, with some combination of
if face.parent.is_a? Sketchup::ComponentDefinition, and if not face.parent.group?, and error trappingQuestion: how do you pick a face inside a closed component (I know how to find things inside closed components using Ruby, but you seem to imply you are doing that via the GUI)? A click on that component should select the component, not a face, regardless of how the component and is axes are oriented. I must be missing something in your description of what you are doing...
On the other hand, if you are doing this all via Ruby, I think that sdmitch has posted the fix in the developer's forum thread you reference. There is no bug - you (and possibly Chris also) simply overlooked the need to transform the normal back to the model's global coordinates.
Steve
-
Sorry I didn't make myself clearer. I'm coding in Ruby. I use the InputPoint.pick method and when I click on a face, it reports via the InputPoint.face method (@ip1.face in the code sample in the onLButtonClick method) whether it has clicked on a face, and if so, which face. But it doesn't say whether it is a 'loose geometry' face, inside a group, or inside a component.
And the reported face.normal differs between a face in a component, and one thas is in a group or loose.
The thing I found (and still find) very confusing is that the reported normal for a face in a rotated component is not the normal to the geometrical face. What is is reported by the .normal method seems by experiment to be the normal to where the face was in World coordinates in the original ComponentDefinition for an un-rotated component. But I suppose on reflection that it is the normal (the Y-axis) in the ComponentInstance's own coordinate system. But I didn't originally see it that way.
I have now worked out how deal with it, by first detecting if the face.parent object is a ComponentDefinition (which equally applies to a Component or a Group), then checking if face.parent.group? is true to see if it is a group, and so not a ComponentInstance) but it seemed originally more complicated and confused me for several hours while I figured out where I needed either the reported World Coordinate normal vector, or the component instance coordinate normal vector.
-
@johnwmcc said:
Sorry I didn't make myself clearer. I'm coding in Ruby. I use the InputPoint.pick method and when I click on a face, it reports via the InputPoint.face method (@ip1.face in the code sample in the onLButtonClick method) whether it has clicked on a face, and if so, which face. But it doesn't say whether it is a 'loose geometry' face, inside a group, or inside a component.
And the reported face.normal differs between a face in a component, and one thas is in a group or loose.
The thing I found (and still find) very confusing is that the reported normal for a face in a rotated component is not the normal to the geometrical face. What is is reported by the .normal method seems by experiment to be the normal to where the face was in World coordinates in the original ComponentDefinition for an un-rotated component. But I suppose on reflection that it is the normal (the Y-axis) in the ComponentInstance's own coordinate system. But I didn't originally see it that way.
I have now worked out how deal with it, by first detecting if the face.parent object is a ComponentDefinition (which equally applies to a Component or a Group), then checking if face.parent.group? is true to see if it is a group, and so not a ComponentInstance) but it seemed originally more complicated and confused me for several hours while I figured out where I needed either the reported World Coordinate normal vector, or the component instance coordinate normal vector.
I believe that what has confused you is that a ComponentInstance does not actually own any entities. Instead, they are all owned by its ComponentDefinition. The things you see in the model view are transformed representations of those entities, not separate objects. That's why when you edit a ComponentInstance the change is instantly repeated in all the other instances of the same ComponentDefinition no matter where they are placed or how they are rotated.
So, when your InputPoint pick finds a Face within a ComponentInstance, the object it returns is actually the original in the associated ComponentDefinition, and the normal to that Face is the normal in the ComponentDefinition's coordinates, not in global model coordinates. You need to transform it back to global coordinates to get the version corresponding to the ComponentInstance you clicked. That is why InputPoint also has a #transformation method to retrieve the transformation associated with whatever it found. This Transformation is an identity if the object is free geometry or in a Group, and is the ComponentInstance's transformation if the object is from one.
So, no offense intended, but the only bug was in your understanding
-
Absolutely no offence taken - it was indeed in my (mis)understanding of what I was seeing. It is now much clearer, and your clarification just sets out very well what I clumsily sort-of-worked-out by experiment and observation in a variety of cases.
I think I was further confused when Chris's plugin seemed to be getting the same unexpected results as I was, and by a rotation I was doing of a view.draw_polyline about the reported face.normal which seemed to be visually wrong - it was actually rotating the polyline in the face plane, about an axis apparently NOT visually normal to it! However, since the only thing that was really bothering me was that when I tried to do a view.draw_line for the normal, it was in the wrong direction, I now understand why, and have fixed it.
Thanks again for your helpful comments.
Has anyone ever done a Tutorial about programming Ruby Tools in SU? I haven't found one, and have done almost all my learning from reading the sample code for CLineTool, and reading and re-reading the API documentation, which is very thin on real world examples and sample outputs. It's been hard work, though ultimately I've got something that now works for rectangular profiles.
Next challenge - to extend it to arbitrary profiles of mouldings. A bit like a simpler version of the Profile Builder plugin from Dale Martin and SMustard - indeed, perhaps in comparison just a poor thing, but mine own, as one of Shakespeare's characters says.
Advertisement