Skip to content

Commit

Permalink
NOTE: New Feature: Rotate|Unconstrained is a trackball style rotation…
Browse files Browse the repository at this point in the history
… tool

contributed by ania, complete with magnets, user input rotation points,
and accessibility from all selection modes.
  • Loading branch information
AnnaWieninger authored and Richard Jones committed Jan 31, 2010
1 parent 4a0bf48 commit 7d4466e
Show file tree
Hide file tree
Showing 2 changed files with 263 additions and 0 deletions.
1 change: 1 addition & 0 deletions plugins_src/commands/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ MODULES= \
wpc_move_planar \
wpc_numeric_camera \
wpc_region \
wpc_rotate_unconstrained \
wpc_shear \
wpc_shift \
wpc_sweep_extrude \
Expand Down
262 changes: 262 additions & 0 deletions plugins_src/commands/wpc_rotate_unconstrained.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
%%
%% wpc_rotate_unconstrained.erl
%%
%% -- Unconstrained Rotation
%% (also known as trackball rotation)
%%
%% Copyright (c) Anna Celarek 2010
%%
%% See the file "license.terms" for information on usage and redistribution
%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
%%

-module(wpc_rotate_unconstrained).
-export([init/0, menu/2, command/2]).
-include("wings.hrl").
-import(lists, [foldl/3]).

init() ->
true.

%% adding the function name into rotate menu
menu({Mode, rotate}, Menu) when Mode =:= vertex; Mode =:= edge; Mode =:= face; Mode =:= body ->
[Menu|[separator,orbitrot_menu(Mode)]];
menu(_, Menu) -> Menu.

%% function name and help (bottom line)
orbitrot_menu(Mode) ->
{?__(1,"Unconstrained"), % function name visible in menu
orbitrot_options(Mode),
{?__(2,"Rotate freely around selection "), [], %% lmb help, [] mmb none,
?__(3,"Pick rotation center")},magnet_possible(Mode)}.

%% magnet (doesn't work yet
magnet_possible(body) -> [];
magnet_possible(_) -> [magnet].

%% lmb, rmb options (have to match with the things after command)
orbitrot_options(body) ->
fun
(1,_Ns) -> {body,{rotate, {unconstrained, lmb}}};
(3,_Ns) -> %'ASK' is an atom too
{body,{rotate, {unconstrained,{rmb,{'ASK',{[center],[],[]}}}}}};
(_,_) -> ignore
end;
orbitrot_options(Mode) ->
fun
(1,_Ns) ->
{Mode,{rotate, {unconstrained,{lmb,{'ASK',{[],[],[magnet]}}}}}};
(3,_Ns) -> % [magnet]={magnet,Type,Route,Point}
{Mode,{rotate, {unconstrained,{rmb,{'ASK',{[center],[],[magnet]}}}}}};
(_,_) -> ignore
end.

%%%% COMMANDS
%% called from menu (see above) or shortkey

% no center, no magnet
command({_,{rotate, {unconstrained,{lmb, {'ASK',{[],_,_}}}}}},St) ->
no_center_setup(none, St);
% no center, magnet
command({_,{rotate, {unconstrained,{lmb, {'ASK',Ask}}}}},St) ->
wings:ask(selection_ask(Ask), St, fun no_center_setup/2);
% no center
command({_,{rotate, {unconstrained,{lmb, Data}}}},St) ->
no_center_setup(Data, St);
% body (has no asks at lmb), no center
command({_,{rotate, {unconstrained, lmb}}}, St) ->
no_center_setup(none, St);
% chose center (and maybe magnet)
command({_,{rotate, {unconstrained,{rmb,{'ASK',Ask}}}}},St) ->
wings:ask(selection_ask(Ask), St, fun center_setup/2);
% Data = {Center, (Magnet)}
command({_,{rotate, {unconstrained,{rmb, Data}}}},St) ->
center_setup(Data,St);
command(_,_) ->
next.

%%%% Asks
selection_ask({Asks,_,_}) ->
selection_ask(Asks);
selection_ask(Asks) ->
Ask = selection_ask(Asks,[]),
{Ask,[],[],[vertex, edge, face]}.
selection_ask([],Ask) -> lists:reverse(Ask);
selection_ask([center|T],Ask) ->
Desc = ?__(1,"Pick center of rotation"),
selection_ask(T,[{point,Desc}|Ask]);
selection_ask([magnet|T],Ask) ->
selection_ask(T,[magnet|Ask]).

%%%% Setup
no_center_setup(_, #st{selmode=body}=St) ->
Center = no_center,
finish_setup(Center, body, St);
no_center_setup(Mag, St) ->
Center = no_center,
finish_setup(Center, Mag, St).

center_setup({Center,_Magnet}, #st{selmode=body}=St) ->
% In case uncon rot + mag is repeated in body mode after being used in v, e, f.
finish_setup(Center, body, St);
center_setup({X,Y,Z}, #st{selmode=body}=St) ->
Center = {X,Y,Z},
finish_setup(Center, body, St);
center_setup({X,Y,Z}, St) ->
Center = {X,Y,Z},
finish_setup(Center, none, St);
center_setup({Center, Mag}, St) ->
finish_setup(Center, Mag, St).

%%%% FOLDING AND DRAGGING
finish_setup(Center0, body, St) -> % body (uses whole matrix instead of single vert coordinates)
Tvs = wings_sel:fold(
fun(_, #we{id=Id}=We, Acc) ->
Center = find_center(Center0, body, We),
[{Id,rotate(body, Center)}|Acc]
end,
[], St),
wings_drag:setup({matrix, Tvs}, [angle, angle], [screen_relative], St);

finish_setup(Center0, Mag, #st{selmode=Mode}=St) ->
Type = magnet_type(Mag),
State = {none,Type,{reciprocal,false}},
CamX = view_cam(x),
CamY = view_cam(y),
Tvs = wings_sel:fold(
fun(Vs0,We,Acc) ->
Vs = conversion(Mode, Vs0, We),
Center = find_center(Center0, Vs, We),
finish_setup_1(Vs,We,CamX,CamY,Center,Mag,State,Acc, St)
end,
[], St),
Units = [angle, angle| magnet_unit(Mag)],
wings_drag:setup(Tvs, Units, flags(State), St).

%%%% vertex list
finish_setup_1(Vs,#we{id=Id}=We,CamX,CamY,Center,none,State,Acc, _) -> % no mag
VsPos = wings_util:add_vpos(Vs,We),
[{Id,{Vs, rotate_fun(CamX,CamY,Center,VsPos,none,State)}}|Acc];

finish_setup_1(Vs, #we{id=Id}=We,CamX,CamY,Center,Mag0,State,Acc, _) -> % mag
{VsInf,Magnet,Affected} = wings_magnet:setup(Mag0, Vs, We),
[{Id,{Affected,rotate_fun(CamX,CamY,Center,VsInf,Magnet,State)}}|Acc].

%%%% Catch view rotations, magnet option changes etc
rotate_fun(CamX,CamY,Center,VsPos,none,State) -> % no mag
fun
(view_changed, NewWe) ->
NewCamX = view_cam(x),
NewCamY = view_cam(y),
NewVsPos = wings_util:update_vpos(VsPos, NewWe),
rotate_fun(NewCamX,NewCamY,Center,NewVsPos,none,State);

([Dx,Dy|_], A) ->
foldl(fun({V,Vpos}, VsAcc) ->
[{V,rotate(Vpos,CamX,CamY,Center,State,Dx,Dy)}|VsAcc]
end,
A, VsPos)
end;

rotate_fun(CamX,CamY,Center,VsInf0,{_,R}=Magnet0,State0) -> % mag
fun
(new_falloff, Falloff) ->
VsInf = wings_magnet:recalc(Falloff, VsInf0, Magnet0),
rotate_fun(CamX,CamY,Center,VsInf,Magnet0,State0);
(new_mode_data, {State, Falloff}) ->
{_,MagType,_} = State,
Magnet = {MagType,R},
VsInf = wings_magnet:recalc(Falloff, VsInf0, Magnet),
rotate_fun(CamX,CamY,Center,VsInf,Magnet,State);
(view_changed, NewWe) ->
NewCamX = view_cam(x),
NewCamY = view_cam(y),
NewVsPos = wings_util:update_vpos(VsInf0, NewWe),
rotate_fun(NewCamX,NewCamY,Center,NewVsPos,Magnet0,State0);
([Dx,Dy|_], A) ->
foldl(fun({V,Vpos,_,Inf}, Acc) ->
[{V,rotate(Vpos,CamX,CamY,Center,State0,Dx*Inf, Dy*Inf)}|Acc]
end,
A, VsInf0)
end.

%%%% ROTATIONS
%%%% (matrix or vector operations)

rotate(Vpos,_CamX,_CamY,_Center,_, 0.0,0.0) ->
Vpos;

rotate(Vpos,CamX,CamY,{Cx,Cy,Cz},_,Dx, Dy) -> % vertex rotation
M0 = e3d_mat:translate(Cx, Cy, Cz),
Angle =math:sqrt(Dx*Dx + Dy*Dy), % mouse radius
Vx = e3d_vec:mul(CamX, Dy), % horizontal
Vy = e3d_vec:mul(CamY, Dx), % vertical
Axis = e3d_vec:norm(e3d_vec:sub(Vy, Vx)), % vector _|_ to mouse direction
M1 = e3d_mat:mul(M0, e3d_mat:rotate(Angle, Axis)),
M = e3d_mat:mul(M1, e3d_mat:translate(-Cx, -Cy, -Cz)),
e3d_mat:mul_point(M, Vpos).

rotate(body, {Cx, Cy, Cz}) -> % body (matrices)
fun(Matrix, [Dx, Dy]) ->
CamX = view_cam(x),
CamY = view_cam(y),
M0 = e3d_mat:mul(Matrix, e3d_mat:translate(Cx, Cy, Cz)),
Angle =math:sqrt(Dx*Dx + Dy*Dy),
Vx = e3d_vec:mul(CamX, Dy),
Vy = e3d_vec:mul(CamY, Dx),
Axis = e3d_vec:norm(e3d_vec:sub(Vy, Vx)),
M1 = e3d_mat:mul(M0, e3d_mat:rotate(Angle, Axis)),
e3d_mat:mul(M1, e3d_mat:translate(-Cx, -Cy, -Cz))
end.

%%%% UTILITIES

%%%% Screen coordinate vector
view_cam(Dir) -> % Dir: x=right, y=up, z=towards camera
#view{azimuth=Az,elevation=El} = wings_view:current(),
M0 = e3d_mat:rotate(-Az, {0.0,1.0,0.0}),
M = e3d_mat:mul(M0, e3d_mat:rotate(-El, {1.0,0.0,0.0})),
e3d_mat:mul_point(M, wings_util:make_vector(Dir)).
%% the last vector does {right, up, towards camera} in screen coordinates

%%%% Convert selection to vertex list
conversion(Mode,Items,We) when not is_list(Items) ->
conversion(Mode, gb_sets:to_list(Items), We);
conversion(vertex,Items,_) ->
Items;
conversion(edge,Items,We) ->
wings_edge:to_vertices(Items, We);
conversion(face,Items,We) ->
wings_face:to_vertices(Items,We).

%%%% Selection center
find_center(no_center, body, We) ->
wings_vertex:center(We);
find_center(no_center, Vs, We) ->
wings_vertex:center(Vs, We);
find_center(Center, _,_) ->
Center.

%%%% Flags for the drag function
flags({_,none,{_,_}}) -> [screen_relative];
flags(State) ->
[{mode,{magnet_modes(),State}}| [screen_relative]].

magnet_type(none) -> none;
magnet_type({_,Type,_,_}) -> Type.

%%%% magnet options (bottom line)
magnet_modes() ->
fun
(help, State) ->
{_,Type,{_,_}}=State,
wings_magnet:drag_help(Type);
({key, K},{none,_Type,_Recip}) when K =:= $1; K =:= $2; K =:= $3; K =:= $4 ->
{none,wings_magnet:hotkey(K),_Recip};
(done,{none,Type,_Recip}) ->
wings_pref:set_value(magnet_type, Type);
(_,_) -> none
end.

magnet_unit(none) -> [];
magnet_unit(_) -> [falloff].

0 comments on commit 7d4466e

Please sign in to comment.