Rotate Component based on axis mouse point of BoundingBox


#1

Hi everybody,
I spent the last two days hard studying Ruby API, and I’m struggling with an issue.

Wanna to do a Tool, like the RotateTool, but it needs to rotate 90 degrees based on the BoundingBox’s component axis.

Ex:
If the mouse is over the top “face” of BoundingBox’s component, it needs to rotate 90 degrees over it’s own Z AXIS.
Just like the rule that appears when choose RotateTool. But don’t need to draw, just to rotate.

I don’t know how to get the center of the BoundingBox side to be the origin of rotation.

I have this for now:

def onLButtonDown(flags, x, y, view)
  ph = view.pick_helper
  ph.do_pick x,y
  element = ph.best_picked

  ip = view.inputpoint x, y
  face=ip.face

  #Based on Bound class of TTLib, maybe here's the problem too, 
  #cause I need the BoundingBox of element, not the bounds of the face
  p1 = face.bounds.corner( 0 )
  p2 = face.bounds.corner( 5 )
  pt = Geom.linear_combination( 0.5, p1, 0.5, p2 )

  rotate = Geom::Transformation.rotation pt, *here_is_the issue*, 90.degrees
  Sketchup.active_model.entities.transform_entities rotate, element
end

here_is_the issue = I know it’s an axis (like [0,0,1] for Z), but how to get the current bounding “mouse over” axis?

That’s it, thanks


#2

Actually, I change the transformation rotation to:

rotate = Geom::Transformation.rotation element.bounds.center, *here_is_the_issue*, 90.degrees

to the element rotates based on your own center. Need to find the axis of the face based on your position, and put this value in “here_is_the_issue” variable.


#3

I’m not certain I understand what you are trying to do, but you may want the Face’s normal vector (Face#normal).


#4

Exactly what I need. Thanks a lot @slbaumgartner.

Just if somebody having the same “problem”, I’ll explain what i’m trying to do in image below:

http://imageshack.com/a/img673/1627/KCyJDZ.png

When put mouse in a side rotate the whole component, based in position of the side, 90º degrees, when perform click.


#5

Well, actually, I need to get “BoundingBox#normal” of the side. Cause I have some components without a face in some sides.


#7

Like this for example:

http://imageshack.com/a/img901/4759/2SM710.png


#8

A bounding box is not real geometry, so its “faces” don’t have properties such as normals. However, you could create a vector normal to a “bb face”. In pseudocode (I’m not where I can write ruby just now):

  1. pick a corner and its two adjacent corners on the desired face of the bb, obtain their Point3d locations from the bb.
  2. create two Vector3d by subtracting the reference corner from each of the others
  3. take the cross-product of these two Vector3d objects to get another Vector3d which will be normal to the selected face of the bb.
  4. make sure you verify the direction of the normal (outward vs inward) so that you know which sign to use in the rotation. One possibility is to compare it with a Vector3d from the third corner of the bb adjacent to the reference corner. Obviously this won’t work if the bounding box has no depth in that direction, but then the Entity is 2D and you should be able to use its normal directly.

[edit] actually, if the object has depth, you could just use the third Vector3d described in step 4. A bounding box is rectangular, so this “edge” is perpendicular to the “face”.


#9

Alright, but how could I get the “bb face”, with point or pickhelper, to pick a corner at step one?


#10

You will get an Entity in the model from the pick, and you will have to use its bounding box. Almost all SketchUp Entities have bounding boxes.


#11

I’m sorry but, how could I know which corner about the side of the boundingBox the mouse is over?
I know how to pick a entire boudingbox, but not the side which the user is clicking.

Maybe I’m not being clear or you already said and I don’t get.


#12

BoundingBoxes are aligned with the global axes, regardless of the orientation of the contents.

Anyway… you can use something similar to:

  element = ph.best_picked

  if element.is_a?(Sketchup::ComponentInstance)
    rotate_instance(element,:z,90.degrees)
  end

def rotate_instance(element,around,degs)
  #
  bb = element.bounds
  tr = element.transformation
  #
  case around
  when :x
    vec = tr.xaxis
  when :y
    vec = tr.yaxis
  when :z
    vec = tr.zaxis
  else
    return false
  end
  #
  element.transform!(
    Geom::Transformation.rotation( bb.center, vec, degs )
  )
  # returns true || false
end

#13

Always thought that this blue box (image below) is the boundingbox. Isn’t it?:
http://imageshack.com/a/img910/3813/MyW9M1.png

Or this blue box is the Group selection and the bb was an imaginary cube arround whole component?


#14

The bounding box of a Group is aligned with the Group’s axes. When you create a Group, its axes are created aligned with the model axes. This has two consequences:

  1. If you subsequently rotate the Group, the axes are rotated and the bounding box follows along, per your picture
  2. If the contents are askew to the model axes when you first create the Group (e.g. if you draw them that way or rotated before Grouping), the bounding box will likewise be askew to the contents. You can edit the Group to realign the contents with respect to its axes, but unlike with a ComponentInstance, there is no tool to reposition the axes with respect to the contents.

#15

Thanks for code, but, my struggle is: how to know the axis (:z in the example above)? Considering that some components will not have a clickable face.


#16

Alright, understood.
So, the problem is bigger than I thought.

Every time the element is rotated, I’ll need to reset the axes of bound? Imagine the image below:
http://imageshack.com/a/img911/9708/pVmkkf.png

If element is in horizontal position, the small side rotates in Y axis, but when rotate and put element in the vertical position, that side will now assume the rotation in Z axis.

So, now has two issues.

  1. How to get the axis of the side that I want, not based on the Sketchup::Face of component, but on the bb side.
  2. Reset the Axis of bb to assume new axis (I think is that what it needs to do).

#17

@slbaumgartner and @DanRathbun

That’s what I’m trying to do:
http://imageshack.com/a/img538/1599/JVDylW.png

Some of my components don’t have a face, even so, I need to be able to select that side to rotate based on his axis.
The same with the top of the component, the face is almost empty, but the user can select that side, regardless of having a face or not.


#18

As mentioned earlier, DrawingElement.bounds - even fro groups and instances - doesn’t represent the selection box you see in the viewport. Instead it’s representative of the bounds of the entity in world space.

You can have a look at an older extension that will create edges or construction lines matching the selection box you see in the viewport:
https://bitbucket.org/thomthom/draw-boundingbox/src/f08e7f6efba7712b56a535e4dae4ade3f9bde7af/src/tt_draw_bb/core.rb?at=master#core.rb-46

http://extensions.sketchup.com/en/content/draw-boundingbox


#19

Yes, I’m looking to your tt_draw_bb code plugin a couple days ago (many thanks for share by the way), pick corner and draw edges or/and faces, working with array of corners, etc.

But my problem is how to know which side the user is clicking when component has no Face.
A tool that is similar of what I’m trying to say is: MovingTool when rotates the component, the tool draw a “+” sign in four positions of a side, regardless of the component’s Face. Like picture I’ve posted before.

http://imageshack.com/a/img538/1599/JVDylW.png

I need to know the side, to pick the “normal” axis and rotate based on that. Like picking the Face#normal vector, to know to where rotation will go based on vector axis.


#20

OK, so what you are trying to do is emulate the magic rotate handles provided by the move tool.

Perhaps a more advanced developer than I knows how to do this in a way that makes sense for a relative newbie (you say you have been studying the Ruby API for only a couple of days - I don’t want to lead you into dynamic drawing using the view’s raw OpenGL methods!), but I would suggest that you consider modifying your attack. The following will work unless there is no Face at all in the ComponentInstance oriented parallel to the desired side of the bounding box:

  1. pick a Face parallel to the desired bb side (the Face does not need to be literally on that side, just parallel to it). Use the normal to this Face as your rotation axis vector. In your illustration, either the face frame or the back could provide such a Face.
  2. backtrack to the ComponentInstance containing the Face using the methods in PickHelper
  3. use the center of the CI’s bounding box as the center for rotation, and the normal from the Face as the axis.

This works because rotation about any point along the rotation axis produces the same effect. That is, it does not matter whether your rotation center is actually the center of the entire bb vs the center of the front face of the bb.


#21

Uhmmmm, never thought that would be so complex.

Yes, kind of. Perhaps doesn’t need to draw the circle rule, just clicking on a side will rotate element 90º automatically.

Was trying to find something like selecting the bounding face, similar to InputPoint#face, thought that InputPoint or PickHelper could have some method that maybe search for a point in the bb.

I’ve downloaded a bunch of plugins that rotates elements and don’t find any that the inputPoint wasn’t a Sketchup::Face.

The pseudocode you wrote is already working, because now, is rotating based on Faces, so if a side is faceless, generally the Face clicked would be the parallel one (sometimes could be the perperdicular one), but need to reset the axes of new rotate position, however this is another question.

Anyway, I will modify my attack like you suggest and work with Face, like has to be.
Thanks a lot for your time and patience.