Find faces component 'lays' on

Is there an easy way to get an array of all the faces that a component lays on?

In the clip below the array returned should be [yellow_face, green_face, blue_face]

If it is a glue_to component it can be glued to a single face, and it is easy to check this, via:
Sketchup::ComponentInstance#glued_to()

If not, you will need to decide what faces to test.

test_faces = ents.grep(Sketchup::Face)

You’ll need to decide what vertices of the component will be “laying down” vertices, and get their 3D point objects in an array, let’s call it lay_pts .

Then iterate the faces checking if any of the lay points are in the faces.

mating_faces = test_faces.find_all {|face|
  lay_pts.any? {|pt|
    result = face.classify_point(pt)
    result != Sketchup::Face::PointOutside &&
    result != Sketchup::Face::PointNotOnPlane &&
    result != Sketchup::Face::PointUnknown
  }
}

Now this would work using the corner vertices, but would likely miss the green face. So you’ll need to add more points at some grid resolution to the lay_pts array.

You might also try the bounding box intersection approach:
Geom::BoundingBox#intersect()

:warning: Pay head to the note about results (of this method) prior to SU2015 !

cbb = cinst.bounds # transformed bounds
lay_bb = Geom::BoundingBox::new.add(
  cbb.corner(0), cbb.corner(1), cbb.corner(2), cbb.corner(3)
)
mating_faces = test_faces.find_all {|face|
  face.bounds.intersect(lay_bb).valid?
}

Note that I do not know what orientation the component or faces are, so you may need to adjust what the corner() argument numbers are. See:
Geom::BoundingBox#corner()

I will experiment with some of these methods. They are all new to me so thanks for your help.

That works very well. The only problem is I need a way to check the faces to see if they match the ‘hole cutting plane’ of the component. This is easy if the component is already glued to a face.

Like this:

mating_faces = test_faces.find_all {|face|
  face.bounds.intersect(cinst.bounds).valid? && 
  (face.plane==cinst.glued_to.plane)
}

I a case where a component is either glued to a group or nothing how can I get:

cint.gluing_plane

Always check the result of Sketchup::ComponentInstance#glued_to() for a nil value, before proceeding.

Ie, to break out of a iteration loop:

break if cinst.glued_to.nil?

or to loop to the next collecton member:

next if cinst.glued_to.nil?

or to bail from a method:

return if cinst.glued_to.nil?

If you glue the component inside the group directly to a face, then the glued_to() query will return a face object instead.

So, apparently the API is lacking a glued_to_face() query method, or perhaps an optional argument for the current method, that would drill into groups and other instances and return the glued to face.


Sketchup::Group objects do not (yet) have a get_glued_instances() method like faces have.

But perhaps you can go at it from the other direction, and use this to iterate the group’s collection of faces, finding that one which returns your instance as the one that is glued.

Where cinst is the component, and group is the object returned by cinst.glued_to():

faces = group.entities.grep(Sketchup::Face)
found = faces.find {|f| f.get_glued_instances.include?(cinst) }
return if found.nil?
glue_plane = found.plane

Thanks.

I already understand how to check what (if anything), a face is glued to. My point rather was that if the component is glued to a face I can get the plane of that face. Otherwise I can compare like this: cint.zaxis == face.normal.
The problem is that a face might be ‘aligned’ but not on the same plane as the component.

I realize that the glued_to face might not always be the same as the gluing plane of the component. It seems strange that SketchUp allows gluing to a face that is not on plane, thereby distorting the face.

What I was really trying to find out; “Is there a way to tell what the gluing plane of a component is, without checking the glued_to face?”

No worries if that is not possible. In my extension I now display a warning and return, if the component is not glued to a face. I just thought it would have been nice to handle this type of situation as well.

BTW: A component can never be glued a face inside another component of group. Or can it? In a case where it appears to be so the result is that the component.glued_to returns the group or component containing the face, and the face.get_glued_instances returns .

Planes in the SketchUp API are represented by arrays.
See: http://www.sketchup.com/intl/en/developer/docs/ourdoc/geom.php
for some handy module methods.

The API defines comparison methods Geom::Point3d#== and Geom::Vector3d#== classes for the 2 element plane array type.

The 4 element array types are Floats representing the coefficent of the plane equation.

See: Euclidean plane - Wikipedia

So it is possible to compare them. But be careful with the Floats. You may need to round them to some certain number of decimals for comparison.

Component definitions (I think) use their local XY plane as the gluing plane.

So for example, you need to draw windows lying flat, when you make them into a gluing component.

Try inserting a Marvin window component and hover over the various faces of a cube. (But don’t click the insertion point.) You’ll see how the “ghost” component changes orientation as you move over the sides or top.

The definition can also be set to glue to faces in any orientation, only horizontal, only vertical, or none (if the component should not allow gluing.)

Other than that, I don’t understand what your trying to say here.

I do not know what this means. I have not seen it. Do you have a screen shot of this, or a sample model showing this has occurred ?

Yes it can, IF the object has been opened for edit, which really means that it is the object’s definition that is been edited. So if you insert a gluing component into an object opened for edit, then ALL instances of that object will show the change.

Thanks for those links. They were very informative. I’ll admit transformations, vectors, and planes, are still confusing to me. If I’d do the research I should, I wouldn’t need to ask for help from nice people like you. :wink:

Slightly annoying if this is not the intended result.

Should probably have been:

This didn’t seem to work, because if a component is glued to a group, it means it is not nested inside it. If this is the case, none of the faces inside the group will return the component as being glued to them.

Thanks for going out of your way to educate me. :slight_smile:

OK, I did say “perhaps”. So there is a missing method for the API.

Just happened to think of it, I actually had added a function for that. I’m sure the same thing could be done for a group.

class Sketchup::ComponentInstance
  def get_glued_instances
    return self.parent.entities.grep(Sketchup::ComponentInstance).find_all {|c| c.glued_to == self}
  end
end

I’m not sure if adding functionality to SketchUp would be condoned in a public extension.

You should never “monkey patch” a class provided in the API, any of the standard Ruby libraries, or any built-in Ruby class. Doing so affects all Ruby code regardless of source and can cause other people’s extensions to fail in strange ways! If you want a custom variation on one of these classes, you should use a refinement instead. @DanRathbun had another recent post about refinements.

It would not, and will not pass inspection for the Extension Warehouse.

For ref: Ruby 2.0.0 doc/syntax/refinements.rdoc


module Author::RefinedComponents

  refine ::Sketchup::ComponentInstance do

    # Returns an array of the externally glued instances if any. (Empty array if none.)
    # @return [Array(Sketchup::ComponentInstance)] the glued instances if any.
    def get_glued_instances()
      parent.entities.grep(Sketchup::ComponentInstance).find_all {|comp|
        (comp.glued_to == self) rescue false
      }
    end

  end # class refinement

end # refinement module


using Author::RefinedComponents

module Author::SomePlugin

  # Use your refinements here.
  
end # author's plugin module

Just FYI, the refinement module definition may be kept in a separate library file, and loaded as needed with require().

1 Like

Ok thanks. I was suspicious of that.

I also could go at it like this:

def get_glued_instances(cinst)
  return cinst.parent.entities.grep(Sketchup::ComponentInstance).find_all {|c| c.glued_to == cinst}
end

gi = get_glued_instances(cinst)
1 Like

Yes, as in a method local to your class or plugin module. This is what many do “in a pinch” just to get the code working.

And it was the preferred pattern before refinements came about in Ruby v2+.

Just an FYI, I have been working on a refinement library. It is in alpha stages at present.

1 Like

Features to make development easier. Nice.

@DanRathbun

Thanks for your help. I got my extension in the warehouse Friday.

Face Cutter Extension

I started a new topic for this issue.