Sketchup::InputPoint #transformation vs. #face inconsistency(?)

In my recent discoveries of the SU Ruby, I’ve been literally “faced” with the following inconsistency, what I would like to understand. I’ll give a possible solution too, but still, I want to understand what’s going on… :slight_smile:

I can’t tell it is a bug or not, but confused me lot and takes 3 days to identify.

My question can be also shortened like this:

Why do I get Sketchup::InputPoint #face object, if the input point is getting its position NOT from a face? The cursor is above the face, but for sure, the input point is not getting its position from that face!

I’ll try to explain.

Background:

“The face method retrieves the face if the input point is getting its position from a face.”

"If you are using the edge, face or vertex method on the InputPoint though, you will probably need to use the transformation method to transform the data that you get back from the selected entity."

Phenomenon:
So if your cursor above the face you will get a Sketchup::InputPoint#face and Sketchup::InputPoint#transformation. The transformation should be related to face since the input point is getting its position from a face. Right?

In most cases, yes. But… It seam to be the InputPoint is still getting the #face object even getting its position NOT from a face but edge or vertex of the face. (or other Inference !) Then the #transformation will be different than the transformation of the face. The inconsistency is here. The InputPoint transformation showing his real transformation (out of the face, but above in screen 2D) but to confuse, still giving you #face object too. You have to think, okay, "I have an @ip.face then the transformation will match to tat face… And if so, why not? "

Reproduce:
Let’s create a face and put it into a group. To show my “problem” it should not be parallel to “main” planes. (test_face.skp attached.)
test_face (1).skp (16.8 KB)

Copy-paste my dirty code to console, position your mouse… and you can see the questionable face id’s and transformations on your screen top left corner.


In this picture it is clear, that the input point getting its position from a point “far away” from the face, but you still get the face object. The ip.transformation is IDENTITY, but the face transformation should be the transformation what you get by pick helper method.

class Tool_test_ipface
  ###dirty!!###
  def initialize
    @ip = Sketchup::InputPoint.new
  end
  
  def onMouseMove(flags, x, y, view)
    @ip.pick(view, x, y)
    # get the face and transformation by ip
    @ip_face = @ip.face
    @tr_ip_face = @ip.transformation

    # get the face and transformation by pick_helper
    # this is a possible method to get the right transformation for the face under cursor
    ph = view.pick_helper
    ph.do_pick(x, y, 0)
    @ph_face = ph.picked_face
    if( @ph_face )
      ph.count.times{|i|
        if ph.leaf_at(i) == @ph_face
          @tr_ph_face = ph.transformation_at(i)
        end
      }	    
    end
    view.invalidate   
  end
  
  def draw(view)
    @ip.draw(view)
    view.line_width = 3
    view.tooltip = @ip.tooltip
    #just to see where are we...
    view.draw_points( @ip.position, 15, 1, "peru" ) if @ip
    # display the face_id:
    view.draw_text((view.corner 0), " ip Face:  " + @ip_face.to_s ) if @ip_face
    view.draw_text((view.corner 0), "\n ph Face: " + @ph_face.to_s ) if @ph_face
    # display the transformation matrix:
    view.draw_text((view.corner 0), "\n\n iF tr:  " + @tr_ip_face.to_a.to_s ) if @ip_face
    view.draw_text((view.corner 0), "\n\n\n pF tr: " + @tr_ph_face.to_a.to_s ) if @ph_face    
   end
end
Sketchup.active_model.select_tool(Tool_test_ipface.new)


This behavior can be mentioned in the documentation and/or reported as a bug? Don’t you think? Or can it be explained on other way?
(At least it might be useful for others…)

I think you are being misled by the InputPoint’s use of inferencing. In your image, there is an inference to the point at the vertex of the side. I can get this effect if I hover the cursor over there and then move it over the face: the inference engine remembers the hover point.

PickHelper does not use inferencing at all, so it is quite possible for it to get different results than InputPoint.

Then the input point getting its position from the hover point. NOT from face. So, please, do not give me the face, give me the #hoverpoint:wink:

Yes, as you can see I discovered that, to be able to get the right transformation…

I can agree it’s a bit odd InputPoint#face returns a face, even when the point is not on it but in front of it.

I remember having this exact problem myself some years ago but can’t remember in what extension :open_mouth: . However, in general I’ve found that you either want to pick an entity without inference, or a point with inference. Seldom you need an entity with inference.

What is your use case?

This may also be relevant, from the API docs about InputPoint:

#transformation ⇒ Object

The transformation method retrieves the Transformation object for the input point.

If the InputPoint object is getting its position from something inside of a component instance, this method returns the Transformation of the component instance. Otherwise it returns the identity Transformation.

Note that the position method on a input point always returns a point that is transformed into model space. If you are using the edge, face or vertex method on the InputPoint though, you will probably need to use the transformation method to transform the data that you get back from the selected entity.


Hi Julia, I’m still working on my own ProLine tool … and as we discussed here:

I’m calculating the centroid of face. I realised I heve to use a transformation if the face is nested inside group(s) or component instance(s) so I thought the transformation from input point would be the okay. But as I discovered I have to be care with it… One of the solution is in my code above.
BTW The other “interesting” behavior: when the inputpoint is on the vertex or edge of the face it is giving back edge+face or vertex+face, but if it is inferring to the edge midpoint or vertex does not give back that point : only the face if the cursor above the face in “screen 2D” space. To figure out this not an easy task if you are refereeing to the (sorry to say) ambiguous documentation…
All in all I discovered it and have a solution. But still confused about docu.
… and again why InputPoint not giving back the " hoverpoint" (From Point in my above mentioned topic), but only this confusing face… :disappointed_relieved:

Believe me I’ve read that a thousand times, even quoted in my first post…
According to this the “selected entity” is a face but is some case you will not get back a transformation of that face. However the input point “lie” to you: “I’m getting my position from face.”

I’d use PickHelper for this.

Perhaps it’s good to also check if the InputPoint position lies on the face, and proceed as if no face were picked at all if it isn’t (as in the image above). I think the user won’t expect a face to be taken into account if the point clearly isn’t on the face.

1 Like

… my current code I’m using both (PickHelper#picked_face and InputPoint#face) to get the right transformation any time :slight_smile:

## Only a portion!!!

def onMouseMove(flags, x, y, view)
  @ip0.pick(view, x, y) if @ip0 && !(@ip1.valid?)
  @ip0.pick(view, x, y, @ip1) if @ip1.valid?
  @tform = (@ip0.transformation) 

  #Find centroid of face
  #(transformation taking into consideration!) 
  #Since SU get ip.face even ip is not really on it (just over it)
  # ip.transformation is suck here, therefore use pick_helper
  if @ip0.face 
    ph_fe = view.pick_helper
    ph_fe.do_pick(x, y, 0)
    @face_c = ph_fe.picked_face
    if @face_c
      ph_fe.count.times{|i|
        if ph_fe.leaf_at(i) == @face_c
          @tr_face_c = ph_fe.transformation_at(i)
          @centroid = highlight_centroid(@face_c, @tr_face_c)
        end
      }	 
    else  
      @centroid = highlight_centroid(@ip0.face,@tform )
    end  
  else
    @centroid = nil
  end
  
  #bla bla
end

#Calculate center point of face (centroid)    
def highlight_centroid(face,tr)
  #Calculate the average of vertices's coordinates 
  # (nested transformation taking into consideration! see above)
  vn =  face.vertices.length
  all = ORIGIN
  face.vertices.each{|v| all += ORIGIN.vector_to(v.position.transform(tr))}
  #this will be the center point of face (centroid)
  Geom::Point3d.new( all.x/vn, all.y/vn, all.z/vn )  
end   

Then I’m nicely highlighting the point and the user can choose to use or not…

I still think this is a matter of misunderstanding InputPoint. Yes, some of what it does is confusing and very poorly documented (PickHelper is also poorly documented!). But I don’t think there is a bug or inconsistency. I prefer to use onLButtonDown to probe the InputPoint, as it is less “jittery” than onMouseMove, but I don’t get any results I can’t explain.

So, some discussion:

To be clear, a Face does not have a Transformation. However, if the Face is in a Group that Group has a Transformation that places it in the Group’s parent. That Transformation will govern how the Face appears in the model’s coordinates and is displayed in the view.

InputPoint#depth (which is an alias for InputPoint#instance_path.length) will tell you whether the Face is loose in the model (InstancePath is empty) or nested inside one or more levels of Groups or Components.

InputPoint#transformation (which is an alias for InputPoint#instance_path.transformation) returns an identity if the Face is loose in the model, and the InstancePath’s full (leaf) Transformation if the Face is nested in side one or more levels of Groups or Components.

InputPoint#position returns a Point3d in the model’s global coordinates. I can’t explain why the designers chose to do that, but they did. To get that point into the same coordinates as the Face, e.g. to test if it is actually on the Face, you need to apply the inverse of the Transformation from the InstancePath.

I myself see nothing odd at all about a method that returns a Sketchup::Face object being named "#face".

It should, … if YOU use the point returned in the onMouseMove callback at the time that the mouse is over the “hoverpoint” you desire. But you will NEVER get that point from a method name "face()" !

Try the the "position()" method.

Me too. :slight_smile: The problem is if the InputPoint # face method retrieves the face EVEN if the input point is getting its position from NOT a face. Please check the picture, or try the attached .skp and code. The input point is getting its position NOT from the face. For sure not. Absolutely not. The returned value of @ip.face must be nil! According to docu:

# face  ⇒ Object

The face method retrieves the face if the input point is getting its position from a face.

Otherwise it returns nil.

This "position()" method will give me the real position of input point. It is clear. In this situation above the input point is getting its position from a “From Point” (NOT FROM ANY FACE) When the input point is getting its position “From Point”. I would like to get that point. And I dont want to get any face ( or ip.face should be return nil) since the ip is not getting its position from any face. Easy, not? :slight_smile:
Sorry but I cant explain more or other way… :slight_smile:

Well then you may need to log both a bug report and a feature request.