-- AxisFromMouseMove -- v 0.8 -- -- author: Christoph 'CrazyButcher' Kubisch -- http://crazybutcher.cottages.polycount.com -- http://www.luxinia.de/index.php/ArtTools/3dsmax -- -- idea: Paul 'MoP' Greveson -- http://www.greveson.co.uk/ -- inspired by LightWave -- -- Retrieves axis constraint thru mousemovement -- when enabled macroscript AxisFromMouseMove category:"CrazyButcher" ( -- some global values we need -- a mouse position couple frames ago global CBmousepos = [0,0] -- how many frames we wait until constraint kicks in -- the more the mpre precise the mouse direction is global CBmouseframes = 8 -- the current frame in the buffer global CBmouseframe = 0 -- what kind of mousemovement we have (dragged, clicked...) global CBmouselastmode = 0 -- means we just set the axis global CBaxisset = false local autoConstraint_Enabled function mopmovetrack = ( local msg = mouse.mode -- when we start a drag -- we calculate which axis to lock if (msg != CBmouselastmode) then ( if (msg == 2 and mouse.buttonstates[1]) then ( -- start a drag CBmouseframe = 0 CBaxisset = false ) if (msg == 0) then ( -- end a drag CBmouseframe = 0 ) CBmouselastmode = msg return 0 ) -- dump current message, so that upper part is only called -- when there is a change CBmouselastmode = msg -- check if we are performing a drag -- and we havent set the axis lock yet if (msg == 2 and mouse.buttonstates[1] and not CBaxisset) then ( if (CBmouseframe == 0) then ( -- we store the current mouse positions and wait a few frame -- the direction travelled might not be precise enough CBmousepos = mouse.screenpos CBmouseframe += 1 ) else if (CBmouseframe < CBmouseframes) then ( CBmouseframe += 1 ) -- buffer is filled, lets set axis else ( CBaxisset = true -- look back a couple frames and take that as direction -- direction is currentpos - startpos local mdir = mouse.screenpos - CBmousepos -- normalize it -- invert Y because mousepos has different axis than -- projected screencoords local dist = 1/sqrt(mdir.x*mdir.x + mdir.y*mdir.y) mdir = [mdir.x*dist, -mdir.y*dist] -- dir is now the 2d vector of the mouse movement -- we now need to find the closest major axis -- to do this we project the major axis into 2d as well -- the view matrix gives us all the major axis after -- projection local mat = getViewTM() local dirx = mat.row1 local diry = mat.row2 local dirz = mat.row3 -- we alre only interested in 2d screen vectors so -- convert to 2d vectors and normalize them dist = 1/sqrt(dirx.x*dirx.x + dirx.y*dirx.y) dirx = [dirx.x*dist, dirx.y*dist] dist = 1/sqrt(diry.x*diry.x + diry.y*diry.y) diry = [diry.x*dist, diry.y*dist] dist = 1/sqrt(dirz.x*dirz.x + dirz.y*dirz.y) dirz = [dirz.x*dist, dirz.y*dist] -- make dotproducts (projects length of vectors) -- also use only absolute values because direction -- (from a to b, vs b to a) should play no -- role, only "best" fit -- the dot product of normalized vectors gives us -- the cosine angle between the vectors -- which means 1 means they have a angle of 0 -- and 0 means an angle of 90° dirx = abs ( dirx.x*mdir.x + dirx.y*mdir.y ) diry = abs ( diry.x*mdir.x + diry.y*mdir.y ) dirz = abs ( dirz.x*mdir.x + dirz.y*mdir.y ) if (dirz > diry and dirz > dirx) then ( toolmode.axisconstraints = #Z ) else if (diry > dirz and diry > dirx) then ( toolmode.axisconstraints = #Y ) else ( toolmode.axisconstraints = #X ) ) ) ) on isChecked return autoConstraint_Enabled on Execute do ( if autoConstraint_Enabled == undefined then autoConstraint_Enabled = true else autoConstraint_Enabled = not autoConstraint_Enabled if autoConstraint_Enabled then ( registerRedrawViewsCallback mopmovetrack ) else ( unRegisterRedrawViewsCallback mopmovetrack ) updateToolbarButtons() )--end execute )-- end macro