Get input point from 3D point

Dear friends,
“position” Method is used to get the 3D point from the input point. Do you know any Method that can get input point from 3D point?

Scroll up a little in a documentation and check the
Constructor Details
how to create the a new InputPoint object.

1 Like
      def onLButtonDown(flags, x, y, view)
        ip1 = Sketchup::InputPoint.new
        ip1.pick view, x, y
        face1 = ip1.face
        p face1
        
        pt2 = Geom::Point3d.new(-3, 0, 2)
        ip2 = Sketchup::InputPoint.new(pt2)
        face2 = ip2.face
        p face2
      end    

We have face1 but we don’t have face2. I click on pt2. Can you help me for it?

From the very beginning of the Class: Sketchup::InputPoint docu, you can read:
The InputPoint used to pick entities that reside under the current cursor location.
Please check out at the end of the page the #valid? method too.

So, You have to call a .pick method on ip2 (similar as you do for ip1 to ‘catch’ valid data what is ‘under’ the mouse cursor location.) Then you can query if there is a face or not…

1 Like

We have a face on pt2. I wish to select that face not face under the mouse cursor location. How can I select that face?

the parentheses were in the wrong place in my previous post… you maybe misunderstand.
Sorry.

You gave a start point for ip2 with ( ip2 = Sketchup::InputPoint.new(pt2) )
Then you have to retrieve the pt2 to screen location
/View.#screen_coords-instance_method

x1, y1, z1 = view.screen_coords(pt2).to_a

Then You have to call a .pick method on ip2 with this coordinates

ip2.pick view, x1, y1 

Then you can query if there is a face or not…

1 Like

Thank you so much. Always you have logical answer for me.

Dear Dezmo,

      def onLButtonDown(flags, x, y, view)
        ip1 = Sketchup::InputPoint.new
        ip1.pick view, x, y
        face1 = ip1.face
        p face1
        p ip1.instance_path.to_a       
        pt2 = Geom::Point3d.new(-3, 0, 2)
        ip2 = Sketchup::InputPoint.new
        x1, y1, z1 = view.screen_coords(pt2).to_a
        ip2.pick view, x1, y1 
        face2 = ip2.face
        p face2
        p ip2.instance_path.to_a    
      end    

face1 and face2 are same. ip1.instance_path return face1 information but ip2.instance_path return nothing. What is my mistake?

I can’t explain that. I have no idea why.I would expect it to return same…

I supposed always face2 will be same but it change when I change view!!! Also ip2.instance_path change when view change. Sometimes return info sometimes don’t. Maybe I am completely in wrong way. I just wish to know group name in pt2 if exist.

you can retrieve the face parent definition and its instances array and get the first instace of it

face2 = ip2.face
# get the parent (it will return Sketchup::ComponentDefinition ... or ..)
p face2.parent
# get the instances of this definition
p face2.parent.instances
# you can use the first one (See note below)
p face2.parent.instances[0]

It maybe works for you in the current situation, but be aware of this:
Copying a group using the copy tool in SketchUp will create copies of the group that share a common definition until an instance is edited manually. If multiple copies are made, all copies share a definition until all copies are edited manually or using
Group#make_unique-instance_method

So more research may be needed …

1 Like

pt2 is a single 3D point and in my example it is only on one face of one group. I supposed in any circumstances this group will be same. face2 and its parents change when for example our group is behind of another group. any solution?

Then you should avoid the InputPoint for face2
As far as I remember Dan already suggest you the Face.classify_point-instance_method

So something like this could work.
NOTE: I wrote it quickly an did not checked at all… just to give an idea!!

def onLButtonDown( flags, x, y, view )
  ip1 = Sketchup::InputPoint.new
  ip1.pick view, x, y
  face1 = ip1.face
  p face1
  p ip1.instance_path.to_a
  pt2 = Geom::Point3d.new(-3, 0, 2)
  # write a new method to get all the faces in a model recursively
  # and call it 
  faces = iterate_ents_for_faces( Sketchup.active_model.entities )
  
  # write a new method to get the first face where the pt2 on it
  # using the face.classify_point instance method
  face2 = get_face_point_on_it( faces, pt2 )

end

def iterate_ents_for_faces( entities, faces = [] )
  entities.each{ |entity|
    case entity
    when Sketchup::Face
       faces << entity
    when Sketchup::ComponentInstance, Sketchup::Group
      iterate_ents_for_faces( entity.definition.entities, faces )
    end
  }
  faces
end

def get_face_point_on_it( faces, point )
  faces.each {|face|
    # use face.classify_point instance method
    result = face.classify_point(point)
    # you might want to consider when the point on the edge 
    # or vertex as well. Then add it to the condition...
    if result == Sketchup::Face::PointInside
      return face
    end
  }
end
1 Like

I check it and It works well. Thank you so much.

You are welcome!
Why the … are you quoting ( put it into ) your post it again! :imp:
Please edit your last post and delete my code from it!

Explanation:
I guess you selected the code to copy it to your computer.
When you are click on the Reply button the forum engine automatically “quote” the selected text into your answer. Please pay attention to this and delete the auto-quote or make sure there is no pre-selected text.

You are right, engine automatically add it. I will be more careful about it. Just I got one problem with your last codes. if faces are in hidden layers,
iterate_ents_for_faces will find them also. How can I solve this problem?

There are lot more… :stuck_out_tongue_winking_eye:
You extensively need to add more conditions, and considering other ‘unforeseen’ behaviors, etc…etc.
For sure!

First of all I strongly recommend to organize your model that basic geometry (faces, edges) are ALWAYS have a Untagged tag. (layer0) Only the groups and components should be tagged differently. In SU the object not ON or not IN a layer (tag) but they have a layer (tag) ‘properties’.
There are some exceptions…
Have you seen this guide?
https://help.sketchup.com/en/sketchup/controlling-visibility-tags


However of course technically possible.
If you check this
Drawingelement#layer-instance_method
and
Layer#visible?-instance_method

E.g. When you are collecting, you can add a condition so you will get a face if it is visible:

...
      faces << entity if entity.layer.visible?
...
1 Like

I need more practice. I review all of our conversations again and again and each time I try for some improvement. I should check all of my codes and check for layers. Thank you.

Dear Dezmo,

      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
            iterate_ents_for_faces( entity.definition.entities, faces )
          end
        }
        faces
      end

      def get_face_point_on_it( faces, point )
        faces.each {|face|
          # use face.classify_point instance method
          result = face.classify_point(point)
          # you might want to consider when the point on the edge 
          # or vertex as well. Then add it to the condition...
          if result == Sketchup::Face::PointInside
            return face
          end
        }
        return nil
      end

Codes work if group (point on it) is made by ruby, but cannot work if group made by SU. Can you help me for it?
It seems I have problem with this line.“iterate_ents_for_faces( entity.definition.entities, faces )”. Codes cannot check faces of a group that made by SU.

This does the same, but more concise …

      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

The Enumerable#find method returns the first matching member of a collection, or nil if not found.


Try this …

      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
2 Likes