forked from bookshelfdave/wings
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NOTE: New Feature: Rotate|Unconstrained is a trackball style rotation…
… tool contributed by ania, complete with magnets, user input rotation points, and accessibility from all selection modes.
- Loading branch information
1 parent
4a0bf48
commit 7d4466e
Showing
2 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]. |