1%% 2%% wpc_rotate_unconstrained.erl 3%% 4%% -- Unconstrained Rotation 5%% (also known as trackball rotation) 6%% 7%% Copyright (c) Anna Celarek 2010-2011 8%% 9%% See the file "license.terms" for information on usage and redistribution 10%% of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11%% 12 13-module(wpc_rotate_unconstrained). 14-export([init/0, menu/2, command/2]). 15-include_lib("src/wings.hrl"). 16-import(lists, [foldl/3]). 17 18init() -> 19 true. 20 21%% adding the function name into rotate menu 22menu({Mode, rotate}, Menu) when Mode =:= vertex; Mode =:= edge; Mode =:= face; Mode =:= body -> 23 [Menu|[separator,orbitrot_menu(Mode)]]; 24menu(_, Menu) -> Menu. 25 26%% function name and help (bottom line) 27orbitrot_menu(Mode) -> 28 Help = {?__(2,"Rotate freely around selection "), [], %% lmb help, [] mmb none, 29 ?__(3,"Pick rotation center")}, 30 {?__(1,"Unconstrained"), % function name visible in menu 31 {rotate_unconstrained,orbitrot_options(Mode, Help)}, magnet_possible(Mode)}. 32 33%% magnet (doesn't work yet 34magnet_possible(body) -> []; 35magnet_possible(_) -> [magnet]. 36 37%% lmb, rmb options (have to match with the things after command) 38orbitrot_options(body, Help) -> 39 fun 40 (help, _Ns) -> Help; 41 (1,_Ns) -> {body,{rotate, {unconstrained, lmb}}}; 42 (3,_Ns) -> %'ASK' is an atom too 43 {body,{rotate, {unconstrained,{rmb,{'ASK',{[center],[],[]}}}}}}; 44 (_,_) -> ignore 45 end; 46orbitrot_options(Mode, Help) -> 47 fun 48 (help, _Ns) -> Help; 49 (1,_Ns) -> 50 {Mode,{rotate, {unconstrained,{lmb,{'ASK',{[],[],[magnet]}}}}}}; 51 (3,_Ns) -> % [magnet]={magnet,Type,Route,Point} 52 {Mode,{rotate, {unconstrained,{rmb,{'ASK',{[center],[],[magnet]}}}}}}; 53 (_,_) -> ignore 54 end. 55 56%%%% COMMANDS 57%% called from menu (see above) or shortkey 58 59% no center, no magnet 60command({_,{rotate, {unconstrained,{lmb, {'ASK',{[],_,_}}}}}},St) -> 61 no_center_setup(none, St); 62% no center, magnet 63command({_,{rotate, {unconstrained,{lmb, {'ASK',Ask}}}}},St) -> 64 wings:ask(selection_ask(Ask), St, fun no_center_setup/2); 65% no center 66command({_,{rotate, {unconstrained,{lmb, Data}}}},St) -> 67 no_center_setup(Data, St); 68% body (has no asks at lmb), no center 69command({_,{rotate, {unconstrained, lmb}}}, St) -> 70 no_center_setup(none, St); 71% chose center (and maybe magnet) 72command({_,{rotate, {unconstrained,{rmb,{'ASK',Ask}}}}},St) -> 73 wings:ask(selection_ask(Ask), St, fun center_setup/2); 74% Data = {Center, (Magnet)} 75command({_,{rotate, {unconstrained,{rmb, Data}}}},St) -> 76 center_setup(Data,St); 77command(_,_) -> 78 next. 79 80%%%% Asks 81selection_ask({Asks,_,_}) -> 82 selection_ask(Asks); 83selection_ask(Asks) -> 84 Ask = selection_ask(Asks,[]), 85 {Ask,[],[],[vertex, edge, face]}. 86selection_ask([],Ask) -> lists:reverse(Ask); 87selection_ask([center|T],Ask) -> 88 Desc = ?__(1,"Pick center of rotation"), 89 selection_ask(T,[{point,Desc}|Ask]); 90selection_ask([magnet|T],Ask) -> 91 selection_ask(T,[magnet|Ask]). 92 93%%%% Setup 94no_center_setup(_, #st{selmode=body}=St) -> 95 Center = no_center, 96 finish_setup(Center, body, St); 97no_center_setup(Mag, St) -> 98 Center = no_center, 99 finish_setup(Center, Mag, St). 100 101center_setup({Center,_Magnet}, #st{selmode=body}=St) -> 102% In case uncon rot + mag is repeated in body mode after being used in v, e, f. 103 finish_setup(Center, body, St); 104center_setup({X,Y,Z}, #st{selmode=body}=St) -> 105 Center = {X,Y,Z}, 106 finish_setup(Center, body, St); 107center_setup({X,Y,Z}, St) -> 108 Center = {X,Y,Z}, 109 finish_setup(Center, none, St); 110center_setup({Center, Mag}, St) -> 111 finish_setup(Center, Mag, St). 112 113%%%% FOLDING AND DRAGGING 114finish_setup(Center, {}, St) -> finish_setup(Center, none, St); 115finish_setup(Center0, body, St) -> 116 %% body (uses whole matrix instead of single vert coordinates) 117 wings_drag:matrix( 118 fun(We) -> 119 Center = find_center(Center0, body, We), 120 rotate(body, Center) 121 end, [angle,angle], [screen_relative], St); 122finish_setup(Center0, Mag, #st{selmode=Mode}=St) -> 123 Type = magnet_type(Mag), 124 State = {none,Type,{reciprocal,false}}, 125 CamX = view_cam(x), 126 CamY = view_cam(y), 127 Units = [angle, angle| magnet_unit(Mag)], 128 wings_drag:fold( 129 fun(Vs0, We) -> 130 Vs = conversion(Mode, Vs0, We), 131 Center = find_center(Center0, Vs, We), 132 finish_setup_1(Vs, We, CamX, CamY, Center, Mag, State) 133 end, Units, flags(State), St). 134 135%%%% vertex list 136finish_setup_1(Vs, We,CamX, CamY, Center, none, State) -> 137 VsPos = wings_util:add_vpos(Vs, We), 138 {Vs,rotate_fun(CamX, CamY, Center, VsPos, none, State)}; 139finish_setup_1(Vs, We, CamX, CamY, Center, Mag0, State) -> 140 {VsInf,Magnet,Affected} = wings_magnet:setup(Mag0, Vs, We), 141 {Affected,rotate_fun(CamX, CamY, Center, VsInf, Magnet, State)}. 142 143%%%% Catch view rotations, magnet option changes etc 144rotate_fun(CamX,CamY,Center,VsPos,none,State) -> % no mag 145 fun 146 (view_changed, NewWe) -> 147 NewCamX = view_cam(x), 148 NewCamY = view_cam(y), 149 NewVsPos = wings_util:update_vpos(VsPos, NewWe), 150 rotate_fun(NewCamX,NewCamY,Center,NewVsPos,none,State); 151 152 ([Dx,Dy|_], A) -> 153 foldl(fun({V,Vpos}, VsAcc) -> 154 [{V,rotate(Vpos,CamX,CamY,Center,State,Dx,Dy)}|VsAcc] 155 end, 156 A, VsPos) 157 end; 158 159rotate_fun(CamX,CamY,Center,VsInf0,{_,R}=Magnet0,State0) -> % mag 160 fun 161 (new_falloff, Falloff) -> 162 VsInf = wings_magnet:recalc(Falloff, VsInf0, Magnet0), 163 rotate_fun(CamX,CamY,Center,VsInf,Magnet0,State0); 164 (new_mode_data, {State, Falloff}) -> 165 {_,MagType,_} = State, 166 Magnet = {MagType,R}, 167 VsInf = wings_magnet:recalc(Falloff, VsInf0, Magnet), 168 rotate_fun(CamX,CamY,Center,VsInf,Magnet,State); 169 (view_changed, NewWe) -> 170 NewCamX = view_cam(x), 171 NewCamY = view_cam(y), 172 NewVsPos = wings_util:update_vpos(VsInf0, NewWe), 173 rotate_fun(NewCamX,NewCamY,Center,NewVsPos,Magnet0,State0); 174 ([Dx,Dy|_], A) -> 175 foldl(fun({V,Vpos,_,Inf}, Acc) -> 176 [{V,rotate(Vpos,CamX,CamY,Center,State0,Dx*Inf, Dy*Inf)}|Acc] 177 end, 178 A, VsInf0) 179 end. 180 181%%%% ROTATIONS 182%%%% (matrix or vector operations) 183 184rotate(Vpos,_CamX,_CamY,_Center,_, 0.0,0.0) -> 185 Vpos; 186 187rotate(Vpos,CamX,CamY,{Cx,Cy,Cz},_,Dx, Dy) -> % vertex rotation 188 M0 = e3d_mat:translate(Cx, Cy, Cz), 189 Angle =math:sqrt(Dx*Dx + Dy*Dy), % mouse radius 190 Vx = e3d_vec:mul(CamX, Dy), % horizontal 191 Vy = e3d_vec:mul(CamY, Dx), % vertical 192 Axis = e3d_vec:norm(e3d_vec:sub(Vy, Vx)), % vector _|_ to mouse direction 193 M1 = e3d_mat:mul(M0, e3d_mat:rotate(Angle, Axis)), 194 M = e3d_mat:mul(M1, e3d_mat:translate(-Cx, -Cy, -Cz)), 195 e3d_mat:mul_point(M, Vpos). 196 197rotate(body, {Cx, Cy, Cz}) -> % body (matrices) 198 fun(Matrix, [Dx, Dy]) -> 199 CamX = view_cam(x), 200 CamY = view_cam(y), 201 M0 = e3d_mat:mul(Matrix, e3d_mat:translate(Cx, Cy, Cz)), 202 Angle =math:sqrt(Dx*Dx + Dy*Dy), 203 Vx = e3d_vec:mul(CamX, Dy), 204 Vy = e3d_vec:mul(CamY, Dx), 205 Axis = e3d_vec:norm(e3d_vec:sub(Vy, Vx)), 206 M1 = e3d_mat:mul(M0, e3d_mat:rotate(Angle, Axis)), 207 e3d_mat:mul(M1, e3d_mat:translate(-Cx, -Cy, -Cz)) 208 end. 209 210%%%% UTILITIES 211 212%%%% Screen coordinate vector 213view_cam(Dir) -> % Dir: x=right, y=up, z=towards camera 214 #view{azimuth=Az,elevation=El} = wings_view:current(), 215 M0 = e3d_mat:rotate(-Az, {0.0,1.0,0.0}), 216 M = e3d_mat:mul(M0, e3d_mat:rotate(-El, {1.0,0.0,0.0})), 217 e3d_mat:mul_point(M, wings_util:make_vector(Dir)). 218 %% the last vector does {right, up, towards camera} in screen coordinates 219 220%%%% Convert selection to vertex list 221conversion(Mode,Items,We) when not is_list(Items) -> 222 conversion(Mode, gb_sets:to_list(Items), We); 223conversion(vertex,Items,_) -> 224 Items; 225conversion(edge,Items,We) -> 226 wings_edge:to_vertices(Items, We); 227conversion(face,Items,We) -> 228 wings_face:to_vertices(Items,We). 229 230%%%% Selection center 231find_center(no_center, body, We) -> 232 wings_vertex:center(We); 233find_center(no_center, Vs, We) -> 234 wings_vertex:center(Vs, We); 235find_center(Center, _,_) -> 236 Center. 237 238%%%% Flags for the drag function 239flags({_,none,{_,_}}) -> [screen_relative]; 240flags(State) -> 241 [{mode,{magnet_modes(),State}}| [screen_relative]]. 242 243magnet_type(none) -> none; 244magnet_type({_,Type,_,_}) -> Type. 245 246%%%% magnet options (bottom line) 247magnet_modes() -> 248 fun 249 (help, State) -> 250 {_,Type,{_,_}}=State, 251 wings_magnet:drag_help(Type); 252 ({key, K},{none,_Type,_Recip}) when K =:= $1; K =:= $2; K =:= $3; K =:= $4 -> 253 {none,wings_magnet:hotkey(K),_Recip}; 254 (done,{none,Type,_Recip}) -> 255 wings_pref:set_value(magnet_type, Type); 256 (_,_) -> none 257 end. 258 259magnet_unit(none) -> []; 260magnet_unit(_) -> [falloff]. 261