Rotation transformation for a component instance
-
I've looked through a bunch of previous posts, but just can't find quite what I need.
I have a custom tool that helps in placing parts.
After clicking for the insertion, I'd like it to rotate the component about its insertion point, relative to Z axis, in relation to where the mouse is.So within my onMouseMove, moving it around is easy enough, inputpoint.pick, and then @comp.transform!(Geom::Transformation.new(ptx)).
So then the user clicks the mouse button, and now the relevant onMouseMove should be applying a rotation, as per the component insertion point (@p0), and the current mouse point (ptx), about the Z axis.
But I'll be darned if I'm having a heck of time doing that!
I mean, I can do this:
@comp.transform!(Geom::Transformation.new(@p0,Z_AXIS,15.degrees))
but that just applies 15 more degrees of rotation to the component, so no matter where you have the mouse, just as you move it, the table just keeps slowly rotating.
So I'm struggling with creating a vector to the new point, ptx, yes? And then I should be able to determine the angle of that vector? And then, its transformation= instead of transform! ?Hoping that is clear enough? I can clean up the code and post if you need.
Thanks, as always, you guys keep saving my rear!
--J
-
I think it sounds like you need to add more logic to watching the cursor location. You appear to have it set so that as the mouse moves a certain amount, it applies a transformation to the component. I think that's good. You just need to decide if you apply a positive or negative 15 degrees I think.
I think what you want to do is watch the screen position of the cursor.
If the x position increases, then:
@comp.transform!(Geom::Transformation.new(@p0,Z_AXIS,15.degrees))
if the x position decreases then:
@comp.transform!(Geom::Transformation.new(@p0,Z_AXIS,-15.degrees))
maybe it looks something like this:
def onMouseMove(flags, x, y, view) if x > @x_old @comp.transform!(Geom;;Transformation.new(@p0,Z_AXIS,15.degrees)) elsif x < @x_old @comp.transform!(Geom;;Transformation.new(@p0,Z_AXIS,-15.degrees)) end @x_old=x end
-
xax=object.transformation.xaxis
will give a vector, initially it's ==X_AXIS, but once it has been rotated you'll need to get 'xax' again.
Then you need to find the flat-angle between that and the cursor-point - let's call that p0 - and the object's insertion point [.origin]...
org=object.transformation.origin p00=p0.clone p00.z=org.z vec=org.vector_to(p00) fangle=xax.angle_between(vec)
Then you have a flat-angle to use in your rotation transformation [perhaps using -ve, I've not tested this!***]
***Also you'll need to usevec.cross(xax)
to determine the flat-angle's 'direction' from the returned cross's z-axis 'sign'... because remember that picking a point 15 degrees away from the 'xax' could be on either side of the object and would return 15 as the answer both times, so the 'cross' cab tell you which side, so you can adjust the fangle +/- ? -
Ugh, still no luck.
Among my issues -- I added a test of creating an edge, just to see what my "target" point was.
By still keeping the .pick method in there, it just keeps "vibrating", jumping around like mad -- I'm not clear on what to test for, whether the ip.pick is too far away -- maybe if the angle calculated is more than, say, 45 degrees from what it is currently?
But then going off of just the x,y from the mousemove, that seems off too, not sure what's wrong with that.
And then the other problem is that the angle_between is always less than 180 degrees, so I guess I do need to use .cross or something, but at a loss for how.
So here's what I have so far, absolutely grateful for any assistance!class PlaceandRotate def initialize @model = Sketchup.active_model @p0 = nil # point about which we rotate site @rotate = nil # if we are rotating site @compdef = @model.definitions[0] # component to be placed @comp = nil # our guy we will be placing @ip = nil # input point end # intialize # activate event fires upon tool usage # load component at 0,0, prep data input points def activate pt0 = Geom;;Point3d.new([0,0,0]) @comp = @model.entities.add_instance(@compdef,pt0) @ip = Sketchup;;InputPoint.new end # activate # as user moves mouse, we apply a transformation to the component. def onMouseMove(flags, x, y, view) if ( @rotate ) # then we are just rotating site about @p0 myx = @comp.transformation.xaxis # current xaxis myz = @comp.transformation.zaxis # this is not working, yet i don't want to have to do an ip.pick here, as it picks up too much clutter # but going off of just x,y is off, not sure how i need to apply view vector or what p00 = [x,y,@p0.z] # mouse position, at same Z as component origin vector = @p0.vector_to(p00) # vector from origin to mouse position on same Z # draw a test edge to confirm our p00 is good @model.entities.add_edges [@p0,p00] # test edge to see if it is # at least all the test edges are correctly on the Z plane! fangle = myx.angle_between(vector) # but only goes up to 180. # need to use V.cross to determine true angle? @comp.transformation = Geom;;Transformation.new(@p0) # reapply our point @comp.transform! Geom;;Transformation.new(@p0,myz,fangle) # rotation # if we instead just do .transformation = .new(@p0,myz,fangle), it goes to 0,0 else # placing # turn off our guy so we don't confuse him with .pick @comp.visible = false # infer our mouse point @ip.pick(view, x, y) ptx = Geom;;Point3d.new(@ip.position) # after picking a clean point without component in the way, turn him back on @comp.visible = true @comp.transformation = Geom;;Transformation.new(ptx) @p0 = ptx # save last point for when rotating end # if end # onMouseMove # L button click, switch to rotating, or place and loop def onLButtonUp(flags, x, y, view) if (!@rotate) # switch to rotating @rotate = true else # then we are placing again! @model.select_tool PlaceandRotate.new end # if end # onLbuttonup # user pressed Esc, erase current plant and exit def onCancel(reason, view) # erase component @model.entities.erase_entities @comp @model.select_tool nil end # onCancel end # PlaceandRotate class def pandr Sketchup.active_model.select_tool PlaceandRotate.new end # def
-
Ah! I'm getting there!
So now I just need to convert angle_between to true angle. Would that be un-normalizing it? Do something with the vectors and compare their reverses or something... hmm...Improved mousemove:
def onMouseMove(flags, x, y, view) # we toggle visibility while picking the point, so it doesn't interfere @comp.visible = false @ip.pick(view, x, y) @comp.visible = true ptx = Geom;;Point3d.new(@ip.position) if ( @rotate ) # then we are just rotating about @p0 myz = @comp.transformation.zaxis # axis we are rotating about ptx = Geom;;Point3d.new([ptx.x,ptx.y,@p0.z]) keep at same Z vector = @p0.vector_to(ptx) # vector from component insertion to mousepoint at same Z fangle = X_AXIS.angle_between(vector) # angle (but only up to 180!) # but how do I determine if it is that, or -1 * that? @comp.transformation = Geom;;Transformation.new(@p0) # if i don't do this first, it goes to 0,0 @comp.transform! Geom;;Transformation.rotation(@p0,myz,fangle) else # placing @comp.transformation = Geom;;Transformation.scaling(@scalex,@scalex,@scalex) @comp.transform! Geom;;Transformation.new(ptx) @p0 = ptx # save last point end # if end # onMouseMove
-
Ah, I see what you were saying TIG.
Like this:crossvector = vector.cross(X_AXIS) multiplier = crossvector[2] if (multiplier < 0) multiplier = 1 else multiplier = -1 end # if # so now our angle is fangle = X_AXIS.angle_between(vector) fangle = fangle * multiplier
No understanding of why that works, but if you say so, then it's the way to do it!
Thanks for help, you rock!
--J
-
Did I misunderstand what you wanted? My code worked well for what I thought you were asking for. Here is your code, with mine added in. I deleted everything you had in the
if ( @rotate )
portion of the onmousemove and replaced it with my snippet from above. Those went in as lines 25-30. I also initialized on line 10 the @x_old variable I introduced.Test this out and see if it does what you had in mind. If not, then I totally misunderstood what you were trying to achieve That wouldn't entirely surprise me!
class PlaceandRotate def initialize @model = Sketchup.active_model @p0 = nil # point about which we rotate site @rotate = nil # if we are rotating site @compdef = @model.definitions[0] # component to be placed @comp = nil # our guy we will be placing @ip = nil # input point @x_old = 0 end # intialize # activate event fires upon tool usage # load component at 0,0, prep data input points def activate pt0 = Geom;;Point3d.new([0,0,0]) @comp = @model.entities.add_instance(@compdef,pt0) @ip = Sketchup;;InputPoint.new end # activate # as user moves mouse, we apply a transformation to the component. def onMouseMove(flags, x, y, view) if ( @rotate ) # then we are just rotating site about @p0 if x > @x_old @comp.transform!(Geom;;Transformation.new(@p0,Z_AXIS,15.degrees)) elsif x < @x_old @comp.transform!(Geom;;Transformation.new(@p0,Z_AXIS,-15.degrees)) end @x_old=x else # placing # turn off our guy so we don't confuse him with .pick @comp.visible = false # infer our mouse point @ip.pick(view, x, y) ptx = Geom;;Point3d.new(@ip.position) # after picking a clean point without component in the way, turn him back on @comp.visible = true @comp.transformation = Geom;;Transformation.new(ptx) @p0 = ptx # save last point for when rotating end # if end # onMouseMove # L button click, switch to rotating, or place and loop def onLButtonUp(flags, x, y, view) if (!@rotate) # switch to rotating @rotate = true else # then we are placing again! @model.select_tool PlaceandRotate.new end # if end # onLbuttonup # user pressed Esc, erase current plant and exit def onCancel(reason, view) # erase component @model.entities.erase_entities @comp @model.select_tool nil end # onCancel end # PlaceandRotate class def pandr Sketchup.active_model.select_tool PlaceandRotate.new end # def
Chris
-
Just a slight misunderstanding, but I wasn't totally clear.
I want to be able to place a component like a bench, and then immediately rotate it about the Z axis.
So both the placement and the rotation should be linked very smoothly to accurate cursor position.
My use of the 15.degrees was just a placeholder, that was actually what I wanted to derive, the angle from the component's insertion point, to the current cursor position.
So I was after a sort of rubber-band style rotation, basically like the Rotate tool. Yours is more of a wheel style, bumping it 15 degrees each time you nudge the mouse. That has its value too, so it's nice to have in the tool basket.
Thanks for the help!--J
-
Ah, got it!
Did you get it worked out now with TIG's code then? Or are you still trying to tidy up some loose end?
Chris
-
@honkinberry said:
I want to be able to place a component like a bench, and then immediately rotate it about the Z axis.
So both the placement and the rotation should be linked very smoothly to accurate cursor position.OH.. well that is extremely EASY.
Because that functionality is built-into the native component placement tool, which is accessible from the Ruby API.
So.. for example the bench's name is "Bench Wood HighBack"
def place_bench() mdl = Sketchup.active_model compdef = mdl.definitions["Bench Wood HighBack"] if compdef mdl.place_component(compdef,false) return true else return false end end
The trick is the 2nd argument to
Model#place_component()
must befalse
, so that you can use the rotate handles (they appear as little red crosses,) just after you choose the insert point.Insert the bench.. then move the cursor to the top of the comp's boundingbox, and you should see 4 red crosses appear. Hover over one of the cross handles, and the cursor will change to a rotate cursor. Hold & Drag (or Click Move Click,) to rotate about the Z axis.
Advertisement