Get input point from 3D point

Unfortunately I got same result. What else can I do?

# encoding: UTF-8
#{ =========================================================
#  Documentation block ...
#} =========================================================

require 'sketchup.rb'
module MajidMahmoudi
  module BoxTest
    class MajBox
      def init()
        @lay = Sketchup.active_model.layers 
        @st = 0
      end  
      def activate
        init()
        Sketchup.active_model.active_layer =  @lay.add('Layer1')
        pt1 = [-5, 0, 0]; pt2 = [5, 0, 0]; pt3 = [5, 10, 0]; pt4 = [-5, 10, 0]
        grp1 = Sketchup.active_model.active_entities.add_group
        face1 = grp1.entities.add_face pt1, pt2, pt3, pt4   
        face1.pushpull -10
      end       

      def onLButtonDown( flags, x, y, view )
        Sketchup.active_model.active_layer =  @lay.add('Layer1')
        ip1 = Sketchup::InputPoint.new
        ip1.pick view, x, y
        pt2 = ip1.position
        faces = iterate_ents_for_faces( Sketchup.active_model.entities )  

        face2 = get_face_point_on_it( faces, pt2 )
        if face2
          p "Face2 = ", face2
        else
          p "Face2 = nil"
        end
      end

      def get_face_point_on_it( faces, point )
        faces.find {|face|
          # use face.classify_point instance method
          face.classify_point(point) == Sketchup::Face::PointInside
        }
      end

      def iterate_ents_for_faces( entities, faces = [] )
        entities.each { |entity|
          case entity
          when Sketchup::Face
            faces << entity if entity.layer.visible?
          when Sketchup::ComponentInstance, Sketchup::Group
            found = iterate_ents_for_faces( entity.definition.entities, faces )
            faces.push(*found)
          end
        }
        faces
      end

    end 
    UI.menu("Plugins").add_item("MajBox") {
    Sketchup.active_model.select_tool(MajBox.new)
    }
  end
end

This codes make a group. If you make another group by SU then click on each group you will got different result in Ruby Consul.

This group have it’s “local coordinate system”. It’s origin (it’s axes intersection) is exactly the same location as the model “global coordinate system”. Therefor the points in it, relative and absolute coordinates numerically will be same.

You might have this group coordinate system elsewhere. Actually this is the situation now.

When you queering the ip1.position , you will get the coordinates in global system, but he clicked face inside the group have a different (local) coordinates.

Fortunately the InputPoint#transformation-instance_method retrieves the Transformation object for the input point.

You have to use inverse transformation on the pt2 in question:
Try with this modification:

...
    face2 = get_face_point_on_it( faces, pt2.transform(ip1.transformation.inverse) )
    if face2
      p "Face2 = #{face2}"
...

TIP: Go to menu
Window>> Model Info >> Component
and check the “Show component axes” to see the “coordinate systems” of components / groups

1 Like

Really interesting problem. Your new codes sometimes work and sometime don’t also it depend on model view. by the way, at the end I wish to find faces that a 3D point is on it I just used inputpoint to show my problem. (I.E we have pt2 and don’t have ip1). Can you help me for it?

InputPoint uses inferencing.
It is very similar (if not the same) as you see, when you are e.g. using the native Line tool.
If the yellow tooltip - beside the coursor - shows the “On face…” you can be sure that the cursor get position on the face. And the first point will be created on face:

But if you see something like “From point” even it looks like the cursor is above the face actually it is not creating the point on face but above…

In your example this kind of tooltip is not implemented. Therefore you will never be sure if the “click” point on the face or not.

There is an other, similar to Imputpoint, the PickHelper method
This one can determine the picked face without a “disturbance” of inferencing.
It has it’s own method to get a transformation at a specific pick path… It is a little more complicated to retrieve but not that much.

You should apply transformation on the 3D point in question, similar as we did in the previous example with pt2, which was determined by Imputpoint position. (I assume 3D point cooordinates given as model (global) cooordinates .)

So both method (in Imputpoint or PickHelper) returns the Transformation of the component instance or group containing the clicked face. You heve to use the inverse of retrieved transformation on the 3D point to get into same coordinate system.

1 Like

My 3D point come from math calculation and not related to InputPoint at all. We have this 3D point in view or not, I wish to know face that this point is on it. As you said, when coordinate system is in it’s origin, codes work good but when use local coordinate system cannot find face. Is it possible at first we change group coordinate system to origin then find face and return coordinate system?

Even I don’t know how to move axes of a group to point 0, 0, 0!!! Although I don’t know it can help or not but when I change in SU, it works. Would you please help me for it?

You have to query the parent group of face, and get the transformation of it.
Then apply this transformation inverse to the questionable point.
E.g.:

def create_box
  pt1 = [-5, 0, 0]; pt2 = [5, 0, 0]; pt3 = [5, 10, 0]; pt4 = [-5, 10, 0]
  grp1 = Sketchup.active_model.active_entities.add_group
  face1 = grp1.entities.add_face pt1, pt2, pt3, pt4 
  face1.pushpull -10
  vector = Geom::Vector3d.new(5, 5, 5)
  tr = Geom::Transformation.translation(vector)
  grp1.transform!(tr)
end

def get_face_point_on_it( faces, point )
  faces.find {|face|
    group = face.parent.instances[0]
    tr = group.transformation
    point_tr = point.transform(tr.inverse)
    p point_tr.to_a
    face.classify_point(point_tr) == Sketchup::Face::PointInside
  }
end

def iterate_ents_for_faces( entities, faces = [] )
  entities.each { |entity|
    case entity
    when Sketchup::Face
      faces << entity if entity.layer.visible?
    when Sketchup::ComponentInstance, Sketchup::Group
      found = iterate_ents_for_faces( entity.definition.entities, faces )
      faces.push(*found)
    end
  }
  faces
end
create_box
faces = iterate_ents_for_faces(Sketchup.active_model.entities)
face_point_on_it = get_face_point_on_it( faces, Geom::Point3d.new(5, 10, 15) )

If the face in a group and this is in a group… etc. You have to do recursively and get the combined transformation.
There is a code already in the forum to how to do that .
I’ll leave you with the joy of searching. :wink:

1 Like

Answered in other topic.

1 Like

You let me enjoy Ruby Sketchup so much. You are great.

1 Like
      def iterate_get_face_point_on_it( point )
        ent = Sketchup.active_model.active_entities
        ent.each{ |entity|
          case entity
          when Sketchup::Face
            if entity.layer.visible?
              result = entity.classify_point(point)
              return entity if result == Sketchup::Face::PointInside 
            end  
          when Sketchup::ComponentInstance, Sketchup::Group 
            if entity.layer.visible?
              tr = entity.transformation
              point_tr = point.transform(tr.inverse)
              entg = entity.definition.entities
              entg.each{ |entityg|
                case entityg
                when Sketchup::Face
                  result = entityg.classify_point(point_tr)
                  return entityg if result == Sketchup::Face::PointInside
                end
              } 
            end
          end  
        }
        return nil
      end

just want to show how much I do practice with your codes. Combined iterate and get. Also I don’t have sub group.

Sorry to bother you again!!! In following if group need transformation, line will draw in different place but face2 is right. Can you tell me how can I solve this problem? (return group reference point to its original).

      def onLButtonDown( flags, x, y, view )
        ip1 = Sketchup::InputPoint.new
        ip1.pick view, x, y
        pt2 = ip1.position
        faces = iterate_ents_for_faces( Sketchup.active_model.entities )  
        face2 = get_face_point_on_it( faces, pt2 )
        grp = face2.parent.instances[0]
        ents = grp.entities
        line = ents.add_line pt2, [pt2.x, pt2.y, pt2.z + 20]        
        if face2
          p "Face2 = #{face2}"
        else
          p "Face2 = nil"
        end
      end

Most probably because :
…position method on a input point always returns a point that is transformed into model space…
as you can read here
InputPoint#transformation-instance_method
You got the face2 right because the method finding it is designed to work with model ordinates of point.

Now - as far as I see - you want to draw a line into a group which have different coordinate system.
Reading further down of above link text:
… 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.
Meaning that you can get the transformation of the point related to the face related to group.
(Note that this transformation is a same as you retrieved earlier for the group)

So something like this should work…

....
        ents = grp.entities
       # corrected
        tr_ip1 = ip1.transformation.inverse
       #corrected
        pt2tr = pt2.transform( tr_ip1 )
        line = ents.add_line pt2tr, [pt2tr.x, pt2tr.y, pt2tr.z + 20] 
....

I hope you have been checked
my other answer for you
about the transformations.

As you realise the transformations are tricky things in SU Ruby API, you need a lot of time , “trial and error” to understand.
Myself just learning too. So, could be some mistakes in my “examples”.

I try to find my problem but cannot find it. I made following ruby code to show my problem.

# encoding: UTF-8
#{ =========================================================
#  Documentation block ...
#} =========================================================

require 'sketchup.rb'
module MajidMahmoudi
  module BoxTest
    class MajBox
      def activate
      end       

      def onLButtonDown( flags, x, y, view )
        ip1 = Sketchup::InputPoint.new
        ip1.pick view, x, y
        pt2 = ip1.position
        grp = iterate_get_face_point_on_it( pt2 )
        ents = grp.entities
        line = ents.add_line pt2, [pt2.x, pt2.y, pt2.z + 20] 
        grp.model.close_active
      end

      def iterate_get_face_point_on_it( point )
        ent = Sketchup.active_model.active_entities
        ent.each{ |entity|
          case entity
          when Sketchup::ComponentInstance, Sketchup::Group 
            p "Group = #{entity}"
            return entity
          end  
        }
        return nil
      end
    end 
    UI.menu("Plugins").add_item("MajBox") {
    Sketchup.active_model.select_tool(MajBox.new)
    }
  end
end

Please make a box as a group (not in global coordinate system)in SU and click on it. You will see line will be in different point but pt2 didn’t change. I need to know problem and only you can help me for it. Thank you in advance.

I did not checked before…So I was wrong, sorry! :blush:
You should apply an inverse transformation…

class MajBox
  def activate
  end       

  def onLButtonDown( flags, x, y, view )
    ip1 = Sketchup::InputPoint.new
    ip1.pick view, x, y
    pt2 = ip1.position
    tr_ip1 = ip1.transformation.inverse
    pt2_tr = pt2.transform(tr_ip1)
    grp = iterate_get_face_point_on_it( pt2 )
    ents = grp.entities
    line = ents.add_line pt2_tr, [pt2_tr.x, pt2_tr.y, pt2_tr.z + 20] 
    # grp.model.close_active
  end

  def iterate_get_face_point_on_it( point )
    ent = Sketchup.active_model.active_entities
    ent.each{ |entity|
      case entity
      when Sketchup::ComponentInstance, Sketchup::Group 
        p "Group = #{entity}"
        return entity
      end  
    }
    return nil
  end
end 
1 Like

I should be more careful about Global and Local coordinate system. Thank you so much

May I ask you to answer my quoestion:
Have been checked
my answer for you in other topic
about the transformations?

I will back to other topic in few days. I need time to finish this topic. For sure many many times I will back to your posts as I did. It is so interesting for me that you answer my many questions before I ask!!!

1 Like

You ask quite succinctly (briefly), so many times I just assume what you want …
That’s why I’m trying to figure out what I would ask if I were in your situation. :grinning:

        tr = grp.transformation
        grp = grp.transform! tr.inverse        
        grpent = grp.definition.entities
        grpent.transform_entities(tr, grpent.to_a)

#     Edit Group

        grpent = grp.definition.entities
        grpent.transform_entities(tr.inverse, grpent.to_a)
        grp = grp.transform! tr

You are great!!!