1%%
2%%  wpc_arc_intersect.erl -- Rotate to Target
3%%
4%%    Plugin to rotate selected element to intersect with a secondary selection
5%%
6%%  Copyright (c) 2008-2011 Richard Jones.
7%%
8%%  See the file "license.terms" for information on usage and redistribution
9%%  of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10%%
11%%
12
13-module(wpc_arc_intersect).
14-export([init/0,menu/2,command/2]).
15-include_lib("src/wings.hrl").
16
17init() ->
18    true.
19
20%%%% Menu
21menu({Mode,rotate},Menu) when Mode =:= vertex; Mode =:= edge; Mode =:= face; Mode =:= body ->
22    [Menu|[separator,arc_intersect_menu(Mode)]];
23menu(_,Menu) ->
24    Menu.
25
26arc_intersect_menu(Mode) ->
27    Help = {?__(2,"Rotate around specified center from Point A to Point B"),
28            ?__(3,"Rotate Point to Plane"),
29            ?__(4,"Rotate around specified center by an angle defined by Vector A and Vector B")},
30    {?__(1,"Rotate to Target"),
31      {arc_intersect, arc_intersect_options(Mode, Help)},magnet_possible(Mode)}.
32
33magnet_possible(body) -> [];
34magnet_possible(_) -> [magnet].
35
36arc_intersect_options(body, Help) ->
37    fun
38      (help,_) -> Help;
39      (1,_Ns) -> {body,{arc_intersect,{lmb,{'ASK',[rotation_axis,center,point_A,point_B]}}}};
40      (2,_Ns) -> {body,{arc_intersect,{mmb,{'ASK',[rotation_axis,center,point,plane]}}}};
41      (3,_Ns) -> {body,{arc_intersect,{rmb,{'ASK',[rotation_axis,center,plane_A,plane_B]}}}};
42      (_,_)   -> ignore
43    end;
44arc_intersect_options(Mode, Help) ->
45    fun
46      (help,_) -> Help;
47      (1,_Ns) -> {Mode,{arc_intersect,{lmb,{'ASK',{[rotation_axis,center,point_A,point_B],[],[magnet]}}}}};
48      (2,_Ns) -> {Mode,{arc_intersect,{mmb,{'ASK',{[rotation_axis,center,point,plane],[],[magnet]}}}}};
49      (3,_Ns) -> {Mode,{arc_intersect,{rmb,{'ASK',{[rotation_axis,center,plane_A,plane_B],[],[magnet]}}}}};
50      (_,_)   -> ignore
51    end.
52
53%%%% Commands
54command({_,{arc_intersect,{lmb,{'ASK',Ask}}}},St) ->
55    wings:ask(selection_ask(Ask), St, fun arc_intersect_setup/2);
56command({_,{arc_intersect,{lmb,Data}}},St) ->
57    arc_intersect_setup(Data,St);
58
59command({_,{arc_intersect,{mmb,{'ASK',Ask}}}},St) ->
60    wings:ask(selection_ask(Ask), St, fun arc_intersect_point_to_plane_setup/2);
61command({_,{arc_intersect,{mmb,Data}}},St) ->
62    arc_intersect_point_to_plane_setup(Data,St);
63
64command({_,{arc_intersect,{rmb,{'ASK',Ask}}}},St) ->
65    wings:ask(selection_ask(Ask), St, fun arc_intersect_plane_setup/2);
66command({_,{arc_intersect,{rmb,Data}}},St) ->
67    arc_intersect_plane_setup(Data,St);
68
69command(_,_) ->
70    next.
71
72%%%% Asks
73selection_ask({Asks,_,_}) ->
74    selection_ask(Asks);
75selection_ask(Asks) ->
76    Ask = selection_ask(Asks,[]),
77    {Ask,[],[],[vertex, edge, face]}.
78selection_ask([],Ask) -> lists:reverse(Ask);
79
80selection_ask([rotation_axis|Rest],Ask) ->
81    Desc = ?__(1,"Pick rotation axis"),
82    selection_ask(Rest,[{axis,Desc}|Ask]);
83selection_ask([center|Rest],Ask) ->
84    Desc = ?__(2,"Pick center of rotation"),
85    selection_ask(Rest,[{point,Desc}|Ask]);
86
87selection_ask([point_A|Rest],Ask) ->
88    Desc = ?__(3,"Pick Point A"),
89    selection_ask(Rest,[{point,Desc}|Ask]);
90selection_ask([point_B|Rest],Ask) ->
91    Desc = ?__(4,"Pick Point B"),
92    selection_ask(Rest,[{point,Desc}|Ask]);
93
94selection_ask([plane_A|Rest],Ask) ->
95    Desc = ?__(5,"Pick Vector A"),
96    selection_ask(Rest,[{axis,Desc}|Ask]);
97selection_ask([plane_B|Rest],Ask) ->
98    Desc = ?__(6,"Pick Vector B"),
99    selection_ask(Rest,[{axis,Desc}|Ask]);
100
101selection_ask([point|Rest],Ask) ->
102    Desc = ?__(7,"Pick Point"),
103    selection_ask(Rest,[{point,Desc}|Ask]);
104selection_ask([plane|Rest],Ask) ->
105    Desc = ?__(8,"Pick Plane"),
106    selection_ask(Rest,[{axis,Desc}|Ask]);
107
108selection_ask([magnet|Rest],Ask) ->
109    selection_ask(Rest,[magnet|Ask]).
110
111%%%% Setup
112%%%% LMB
113arc_intersect_setup({Axis,Center,A,B},St) ->
114    arc_intersect_setup({Axis,Center,A,B,none},St);
115
116arc_intersect_setup({Axis,Center,A,B,Mag},#st{selmode=body}=St) when Mag =/= none ->
117    arc_intersect_setup({Axis,Center,A,B,none},St);
118
119arc_intersect_setup({Axis,Center,A,B,Mag},St) ->
120    VecA = double_cross(Center,A,Axis),
121    VecB = double_cross(Center,B,Axis),
122    Deg = e3d_vec:degrees(VecA,VecB),
123    finish_setup(Axis, Center, Deg, Mag, St).
124
125%%%% MMB
126arc_intersect_point_to_plane_setup({Axis,Center,A,B},St) ->
127    arc_intersect_point_to_plane_setup({Axis,Center,A,B,none},St);
128
129arc_intersect_point_to_plane_setup({Axis,Center,A,B,Mag},#st{selmode=body}=St) when Mag =/= none ->
130    arc_intersect_point_to_plane_setup({Axis,Center,A,B,none},St);
131
132arc_intersect_point_to_plane_setup({Axis,Center,A,B,Mag},St) ->
133    VecA = double_cross(Center,A,Axis),
134    VecB = e3d_vec:cross(Axis,B),
135    Deg = e3d_vec:degrees(VecA,VecB),
136    finish_setup(Axis, Center, Deg, Mag, St).
137
138%%%% RMB
139arc_intersect_plane_setup({Axis,Center,A,B},St) ->
140    arc_intersect_plane_setup({Axis,Center,A,B,none},St);
141
142arc_intersect_plane_setup({Axis,Center,A,B,Mag},#st{selmode=body}=St) when Mag =/= none ->
143    arc_intersect_plane_setup({Axis,Center,A,B,none},St);
144
145arc_intersect_plane_setup({Axis,Center,A,B,Mag},St) ->
146    Deg = e3d_vec:degrees(A, B),
147    finish_setup(Axis, Center, Deg, Mag, St).
148
149finish_setup(Axis, Center, Deg, Mag, #st{selmode=Selmode}=St0) ->
150    St = case Selmode of
151        vertex -> St0;
152        _Other -> wings_sel_conv:mode(vertex,St0)
153    end,
154    MagType = magnet_type(Mag),
155    State = {none,MagType,{reciprocal,false}},
156    Units = [percent|magnet_unit(Mag)],
157    Flags = [{mode,{arc_intersect_modes(Mag),State}}],
158    wings_drag:fold(
159      fun(Vs0, We) ->
160              Vs = gb_sets:to_list(Vs0),
161              finish_setup_1(Vs, We, Axis, Center, Deg, Mag, State)
162      end, Units, Flags, St).
163
164finish_setup_1(Vs, We, Axis, Center, Deg, none, State) ->
165    VsPos = wings_util:add_vpos(Vs, We),
166    {Vs,arc_intersect_fun(Axis, Center, Deg, VsPos, none, State)};
167finish_setup_1(Vs, We, Axis, Center, Deg, Mag, State) ->
168    {VsInf,Magnet,Affected} = wings_magnet:setup(Mag, Vs, We),
169    {Affected,arc_intersect_fun(Axis, Center, Deg, VsInf, Magnet, State)}.
170
171magnet_unit(none) -> [];
172magnet_unit(_) -> [falloff].
173
174magnet_type(none) -> none;
175magnet_type({_,Type,_,_}) -> Type.
176
177arc_intersect_modes(none) ->
178    fun
179      (help, State) -> arc_intersect_mode_help(State);
180      ({key,$1},{none,none,{reciprocal,false}}) -> {none,none,{reciprocal,true}};
181      ({key,$1},{none,none,{reciprocal,true}})  -> {none,none,{reciprocal,false}};
182      (_,_) -> none
183    end;
184
185arc_intersect_modes(_) ->
186    fun
187      (help, State) -> arc_intersect_mode_help(State);
188      ({key,$5},{none,_Type,{reciprocal,false}}) -> {none,_Type,{reciprocal,true}};
189      ({key,$5},{none,_Type,{reciprocal,true}})  -> {none,_Type,{reciprocal,false}};
190      ({key, K},{none,_Type,_Recip}) when K =:= $1; K =:= $2; K =:= $3; K =:= $4 ->
191          {none,wings_magnet:hotkey(K),_Recip};
192      (done,{none,Type,_Recip}) -> wings_pref:set_value(magnet_type, Type);
193      (_,_) -> none
194    end.
195
196arc_intersect_mode_help({_,none,{_,Flip}}) ->
197    ?__(1,"[1] ") ++ flip_help_1(Flip);
198arc_intersect_mode_help({_,Type,{_,Flip}}) ->
199    wings_magnet:drag_help(Type)++flip_help(Flip).
200flip_help(Flip) ->
201    ?__(1,"  [5] ") ++ flip_help_1(Flip).
202flip_help_1(true) ->
203    ?__(1,"Flip Angle");
204flip_help_1(false) ->
205    ?__(2,"Flip Back").
206
207arc_intersect_fun(Axis,Center,Deg,VsPos,none,State) ->
208    fun
209      (new_mode_data,{NewState,_}) ->
210          arc_intersect_fun(Axis,Center,Deg,VsPos,none,NewState);
211      ([Percent|_], A) ->
212          lists:foldl(fun({V,Vpos}, VsAcc) ->
213          [{V,arc_intersect(Axis,Center,Deg,Vpos,State,Percent)}|VsAcc]
214          end, A, VsPos)
215    end;
216
217arc_intersect_fun(Axis,Center,Deg,VsInf0,{_,R}=Magnet0,State0) ->
218    fun
219        (new_falloff, Falloff) ->
220         VsInf = wings_magnet:recalc(Falloff, VsInf0, Magnet0),
221         arc_intersect_fun(Axis,Center,Deg,VsInf,Magnet0,State0);
222
223         %% Magnet Type Switch
224         (new_mode_data, {State, Falloff}) ->
225         {_,MagType,_} = State,
226         Magnet = {MagType,R},
227         VsInf = wings_magnet:recalc(Falloff, VsInf0, Magnet),
228         arc_intersect_fun(Axis,Center,Deg,VsInf,Magnet,State);
229
230        ([Percent|_], A) ->
231            lists:foldl(fun({V,Vpos,_,Inf}, Acc) ->
232            [{V,arc_intersect(Axis,Center,Deg,Vpos,State0,Percent*Inf)}|Acc]
233            end, A, VsInf0)
234    end.
235
236
237arc_intersect(_Axis,_Center,_Deg,Vpos,_State,0.0) ->
238    Vpos;
239
240arc_intersect(Axis,Center,Deg,Vpos,_State,Percent) when Deg =:= 0.0; Deg =:= 180.0 ->
241    Rotate = 180.0 * Percent,
242    rotate(Vpos,Axis,Center,Rotate);
243
244arc_intersect(Axis,Center,Deg,Vpos,{_,_,{_,false}},Percent) when Percent < 0.0 ->
245    Rotate = Deg * Percent,
246    rotate(Vpos,Axis,Center,Rotate);
247
248arc_intersect(Axis,Center,Deg,Vpos,{_,_,{_,false}},Percent) when Percent > 0.0 ->
249    Rotate = (180.0 - Deg) * Percent,
250    rotate(Vpos,Axis,Center,Rotate);
251
252arc_intersect(Axis,Center,Deg,Vpos,{_,_,{_,true}},Percent) when Percent < 0.0 ->
253    Rotate = (180.0 - Deg) * Percent,
254    rotate(Vpos,Axis,Center,Rotate);
255
256arc_intersect(Axis,Center,Deg,Vpos,{_,_,{_,true}},Percent) when Percent > 0.0 ->
257    Rotate =  Deg * Percent,
258    rotate(Vpos,Axis,Center,Rotate).
259
260%%%% Utilities
261double_cross(Center,Point,Axis) ->
262    Vec0 = e3d_vec:sub(Center,Point),
263    Vec1 = e3d_vec:cross(Vec0, Axis),
264    e3d_vec:cross(Vec1, Axis).
265
266rotate(Vpos,Axis,{Cx,Cy,Cz},Angle) ->
267    %% return new position as {x,y,z}
268    A0 = e3d_mat:translate(Cx,Cy,Cz),
269    A1 = e3d_mat:mul(A0, e3d_mat:rotate(Angle, Axis)),
270    A2 = e3d_mat:mul(A1, e3d_mat:translate(-Cx,-Cy,-Cz)),
271    e3d_mat:mul_point(A2,Vpos).
272