Locating Objects correctly when iterating Child Entities - Section Plane with Materials/Patterns

Hello All,

I am working on a script that, until Sketchup does this natively, allows me to assign a material (with pattern) to a section cut.

It creates a cutting plane (basically a giant circle using the model bounds) at the level of certain section planes and intersects it with objects in the model meeting my criteria.

So first it descends into certain groups and collects qualifying child entities into an array called @@ents_to_cut. Qualifying child entities may be nested within several parent entities.

Then using intersect_with() it creates edges where the cutting plane meets @@ents_to_cut.

I’m having problems getting the resulting section to end up in the correct location, I think because I am not tracking transformations correctly as I descend into each parent.

Here is how I am doing it currently…

# track transformations as ents are probed
@@transformation_array = [Geom::Transformation.new()]

def self.probe_for_solids(parent_ent)

    #print "\n------------probe_for_solids for parent_ent: #{parent_ent}------------\n\n"

    # probe ents meeting main criteria
    if
        parent_ent.is_a? Sketchup::Group &&
        (
            parent_ent.layer.name.include?('EXIST') ||
            parent_ent.layer.name.include?('PROP') ||
            parent_ent.layer.name.include?('OPT') ||
        )

        print "probing #{parent_ent.name} - containing #{parent_ent.entities.length} children\n"

        #iterate children to find manifold solids
        parent_ent.entities.each_with_index{|child_ent, index|

            if child_ent.is_a?(Sketchup::Group) && child_ent.manifold?

                print "keeping child group #{index}: #{child_ent.name} because it is manifold\n"

                this_transformation = child_ent.transformation

                @@ents_to_cut.push([child_ent,parent_ent.layer.name,this_transformation])

            elsif child_ent.is_a?(Sketchup::ComponentInstance) || child_ent.is_a?(Sketchup::Group)

                print "child #{index}: is non-manifold group or component - probing deeper\n"

                #store the transformation of the current parent entity
                @@transformation_array.push(child_ent.transformation);

                #send back to this function for deeper probing
                probe_for_solids(child_ent)

            end

        }

    else

        print "#{parent_ent} - skipping non qualifying entity \n"

    end

end

And here is the actual intersection…

cutting_circle.entities.intersect_with(
    false,
    ent_to_cut[2],
    @@section_container,
    @@section_container.transformation,
    false,
    ent_to_cut[0]
 )

So, I have tried all of the following definitions of `this_transformation’ and I’m not having any success.

                #this_transformation = parent_ent.transformation * child_ent.transformation
                #this_transformation = parent_ent.transformation
                #this_transformation = ORIGIN

and these actually do not produce a section at all!

                #this_transformation = @@transformation_array.inject(:*) * child_ent.transformation
                #this_transformation = @@transformation_array.inject(:*) * parent_ent.transformation
                #this_transformation = @@transformation_array.inject(:*)

Any tips would be greatly appreciated. Thanks.

FYI, the 2nd argument to #intersect_with expects a Sketchup::Entities collection object.
Are you passing a single entity?


Also see:


You need to be aware that the API’s getter methods do not always return coordinates in the same system.

When the model.active_entities == model.entities (ie, the user is at the top level) then getter methods return positions in the instance’s local coordinate system.

But weirdly, when the user drills down into editing contexts, the API positions returned are in world coordinate system.

The transforms (or some of their properties) may also be affected this way. (ie, origin, etc.)

Thanks Dan. I think you are on to something there. I am indeed sending each qualifying entity to intersect_with individually. I should probably be doing it for the whole collection.

I will update that and see if that is the issue.

So active_path is really cool! That means, rather than descending into individual objects and keeping track of the ancestry as you go, you can recall it later on when you get an object you want. That’s awesome, thanks.

Thanks as always!

Rob

1 Like

Strangely, running intersect_with on the entire collection of qualifying solids made no difference to the location of the resulting section!

I had said …

I really meant the 3rd argument. It is the entities collection into which you wish to put the intersection lines.

Your snippet may have actually been correct as it passes @@section_container as long as this reference is to an entities collection object. If it is a group that you’ve made to hold the intersection lines, you may need to put a temp cpoint in it, and then pass @@section_container.entities for the 3rd method.

Your snippet above said …

cutting_circle.entities.intersect_with(
    false,
    ent_to_cut[2],
    @@section_container,
    @@section_container.transformation,
    false,
    ent_to_cut[0]
 )

… which implies that the 2nd argument you pass is an entity, but the method expects a transform for that parameter.

This is what I saw when I asked …

So I’d think that the 2nd argument would be …
cutting_circle.transformation

:question:

Ahh… OK, I’ll re-jigger. Thanks.

1 Like

Still fighting it. The sections created show up in the right place only if the group is located at the origin.

I think I don’t understand how it works when I loop through all the models entities and add some of them to the @@ents_to_cut array. It seems like I am just ‘capturing’ a handle to each qualifying entity. But when that entity collection (array?) is referenced later, how does the script deal with the fact that the collection’s entities may be in 20 different contexts (editing levels)?

Correct. It is called a reference. It points at a Ruby object.

SketchUp Drawingelement Entity objects can ONLY be “held by” or “be a member of” an Entities collection.

No. The two are not the same thing. Although (in many computer languages) an Array class might be generally considered a Collection type, … a collection is really is a set of (unique) things that belongs to a parent object. The model owns quite a few collections of things (tag-layers, styles, definitions, materials, entities, etc.)
Because a model can also be a component (saved as a .skp file,) component definitions also have entities collections.

Just because you identify entity objects via iterator or filter methods and store references to them in an array, does not mean you have removed them from the “owner” collection and put them “in the array”. The array holds only references that point at the objects (still in the collection.)

Again your Ruby array is actually not a collection in the C++ sense (which is what SketchUp core is written in.) Understand that the Ruby API exposes SketchUp’s C++ objects and collections for scripting. (Our Ruby references are really pointing at C++ objects “behind the curtain”.)

I myself would never do intersects with objects from different entities contexts as they are in differing coordinate systems.

Do each intersect separately with entities from each entities level adding them successively to your target entities object.