Global bounding boxes


#1

When getting the center point to a bounding box, it appears to always be relative to the group’s coordinate system, not the global coordinate system. Then, if you select the group the object is contained in, and check the bound center again, it’s now globally aligned.

In the above image, the left cube’s center X is at -1.24m. However, selecting the cube and checking the bounds gives me the following incorrect, relative center:

Sketchup.active_model.entities[0].entities[0]
1
Sketchup.active_model.selection[0].bounds.center.x
~ 0.58m

Is there a way to get the correct global bounding center for an object contained within a group?

I’d rather not just simply apply the transformation for the parent group, since the actual models I’m working with will likely have many levels of groups, and I’m guessing right now that the issue will continue to compound. Not to mention that you can’t simply find the parent group either.

The purpose of this is collision detection between various objects anywhere in the model, where I need the accurate global position of each object.


#2

The first line of your code obtains an entity nested inside of a group.

outer_entity = Sketchup.active_model.entities[0]
inner_entity = Sketchup.active_model.entities[0].entities[0]
inner_center = inner_entity.bounds.center
center = inner_center.transform( outer_entity.transformation )

Applying the outer group’s “transformation” to a position in its local coordinate system converts it to the outside coordinate system. Your code above shows that you want the outer bounding box of the nested entity (there also is a “local_bounds” method for a group).

I did not use your “…selection[0]” statement in my code, as that is trickier. The only way that the selection tool can select a group inside another group is if your are in edit mode on the outer group. Your included image does not appear to be in group edit, in that case your selection statement obtains the X coordinate of the bounding box center of the outer group.

For collision detection, you might consider using the bounding box class’s “intersect” method.


#3

“For collision detection, you might consider using the bounding box class’s “intersect” method.”

Except that if I push the front legs of my chair under the dining table, to 2 objects are not colliding, but their bounding boxes intersect. And so on for many real world objects.


#4

that doesn’t exclude using bounds.intersect for collision detection, it is good for sorting out a target list…

mod = Sketchup.active_model
ent = mod.entities
grp = ent.grep(Sketchup::Group)

intersections = []

for i in 0...grp.length - 1
  grp.each{ |g| next if g == grp[i] # skip comparing to self
               bb = Geom::BoundingBox.new.add(g.bounds.intersect(grp[i].bounds))
               if bb.valid?
                  puts "#{g.name} and #{grp[i].name} intersect "
                  sz1 = g.entities.grep(Sketchup::Face).length + grp[i].entities.grep(Sketchup::Face).length
                  intersections << [g,grp[i],sz1]
                else
                  puts "#{g.name} and #{grp[i].name} do not intersect"
                end
              bb.clear
             }
end

puts "but do they overlap?\n"

returns
chair two and chair one do not intersect
Table and chair one intersect
chair three and chair one do not intersect
chair one and chair two do not intersect
Table and chair two intersect
chair three and chair two do not intersect
chair one and Table intersect
chair two and Table intersect
chair three and Table intersect
but do they overlap?

then using

intersections.each{|g|
                     if g[0].valid? and  g[1].valid?
                       a = g[0].name.dup
                       b = g[1].name.dup
                       sz1 = g[2]
                       
                       mod.start_operation("test using outer shell")
                       new_grp = g[0].outer_shell(g[1])
                       sz2 = new_grp.entities.grep(Sketchup::Face).length
                       puts "Geometry Overlaps in #{a} and #{b}" if  sz1!= sz2
                       mod.abort_operation()
                     end
                   }
nil

returns
Geometry Overlaps in Table and chair two

here’s the model I tested on…
table_n_chairs.skp (48.1 KB)

john


#5

I’m not sure I understand:

bb = Geom::BoundingBox.new.add(g.bounds.intersect(grp[i].bounds))

wouldn’t

bb = g.bounds.intersect( grp[i].bounds )

provide a bounding box?

The code above is using the outer bounding boxes of the groups. It will work well when their transformation axes line up with the world axes. I have a plugin that I developed that can show me both the outer bounding box and the local one. You could consider using the local bounding boxes. I opened @john_drivenupthewall’s file, changed the style to simple, and rotated the end chair more. Below are two images that my tool provides. The left dashed cyan one in is an outer bounding box, while the right solid yellow one is the “local bounds”. (The local bounds appears to be what the selection tool outlines):


The local bounds corners are in local coordinates, so to work with them would require converting all local bounding boxes to outer coordinates.
Note: The solid magenta line is the transformation’s local X axis, and the neon green one the Y axis. They connect at the transformation’s origin.


#6

yes, this was an old snippet looking for a way around a < 14 bug

the second half a just chucked together as an example of using collected bounds…

It’s definitely not production ready code…
using outer_shell as a check could be quite expensive for big models…

[quote=“BruceYoung, post:5, topic:16846”]
using the local bounding boxes
[/quote]it would certainly tighten the group of candidates in some cases…

@BruceYoung, did the example work for you? I only tested on a mac…


#7

In my scenario, I’m doing the collision detection in a separate library, not directly in Ruby. Currently, the center point is the only data point that is coming back incorrect due to groups.

@BruceYoung If the entity I’m calculating the center for is 5 groups deep for instance, I’m guessing I need to apply all 5 transforms, in order, to get the accurate center point?


#8

@john_drivenupthewall
The first loop worked okay (Sketchup 2015 on Windows 7). The second loop, worked after I ran it the third time. Each time I run it I’m also observing:

Not sure why. Maybe my copy and paste wasn’t quite right.


#9

@weddins
Yes, you will need to apply all transformations. The order that they are applied is the from the most inner to the most outer. For example, three transformations be combined into one with:

total_transform = outer_t * middle_t * inner_t

You can skip the entity’s own transformation if you are using the outer bounds. You may not need to proceed all the way to the outer most world coordinates if you intend to compare entities that are in a common group (as long as they are relative to the same coordinate system).


#10

just looks like you missed the nil on last line…


#11

Okay, the nil is suppressing the “abort_operation” output.

I reloaded and ran it multiple times. I’m seeing the same problem of the final result only being displayed one out of three runs or so. I added an extra “puts” before the intersection loop to state the “intersections” array length. It is usually 5 and always non-zero, but the odd time I’m seeing it a length of 3.


#12

Also be aware of the untransformed bounding box all definitions have. (Groups are really special component instances, so they also have a definition.)

see: Group#local_bounds

Perhaps ? (or similar) …

gcen = grp.local_bounds.center.tranform(grp.transformation)

The actual bounding boxes of instances can very depending upon their rotation. The bounding boxes are always aligned with the “world” axes. So they get bigger as the instance is rotated, and the center point changes.