1%%
2%%  wpc_explode.erl --
3%%
4%%    Explode scales distances between whole objects.
5%%    Includes standard, user axes, radial and uniform options.
6%%
7%%  Copyright (c) 2010-2011 Richard Jones.
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_explode).
14-export([init/0,menu/2,command/2]).
15-include_lib("src/wings.hrl").
16
17-import(lists, [reverse/1]).
18
19init() ->
20    true.
21
22%%%
23%%% Insert menu heading
24%%%
25
26menu({body},Menu) ->
27    reverse(parse(Menu, [], false));
28menu(_,Menu) ->
29    Menu.
30
31parse([{_,{flip,_}}=A|Rest], NewMenu, false) ->
32    parse(Rest, [menu_heading(),A|NewMenu], true);
33parse([Elem|Rest], NewMenu,  Found) ->
34    parse(Rest, [Elem|NewMenu], Found);
35parse([], NewMenu, true) ->
36    NewMenu;
37parse([], NewMenu, false) ->
38    [menu_heading()|NewMenu].
39
40menu_heading() ->
41    {?__(1,"Explode"),{explode,explode_menu()}}.
42
43%%%
44%%% Build menus
45%%%
46
47explode_menu() ->
48    fun(help,_) ->
49           {?__(1,"Space objects along a standard axis relative to a point"),
50            ?__(2,"Pick axis and point"),
51            ?__(3,"Pick axis and explode from midpoint of the selection")};
52       (1,_) -> standard_axes();
53       (2,_) -> {body,{explode,{'ASK',[axis,point]}}};
54       (3,_) -> {body,{explode,{'ASK',[axis]}}};
55       (_,_) -> ignore
56    end.
57
58standard_axes() ->
59     [axis_menu(x),
60      axis_menu(y),
61      axis_menu(z),
62      separator,
63      axis_menu(uniform),
64      axis_menu(radial),
65      separator,
66      axis_menu(last_axis),
67      axis_menu(default_axis)].
68
69axis_menu(radial) ->
70    AxisStr = ?__(6,"Radial"),
71    Fun = fun
72      (help,_) ->
73        {?__(3,"Explode objects along radial of standard axis"),
74         ?__(4,"Pick radial and point"),
75         ?__(5,"Pick radial and explode from midpoint of the selection")};
76      (1,_) -> radial_axis();
77      (2,_) -> {body,{explode_radial,{'ASK',[axis,point]}}};
78      (3,_) -> {body,{explode_radial,{'ASK',[axis]}}};
79      (_,_) -> ignore
80    end,
81    {AxisStr,{explode_radial,Fun}};
82axis_menu(uniform) ->
83    AxisStr = wings_s:dir(uniform),
84    Fun = fun
85      (help,_) ->
86        {?__(7,"Explode objects uniformly from midpoint of the selection"),[],
87         ?__(2,"Pick point")};
88      (1,_) -> {body,explode_uniform};
89      (3,_) -> {body,{explode_uniform,{'ASK',[point]}}};
90      (_,_) -> ignore
91    end,
92    {AxisStr,{explode_uniform,Fun}};
93axis_menu(Axis) ->
94    AxisStr = wings_util:cap(wings_s:dir(Axis)),
95    Fun = fun
96      (help,_) ->
97        Help0 = ?__(1,"Explode objects along ~s axis from midpoint of the selection"),
98        Help = wings_util:format(Help0, [AxisStr]),
99        {Help, [], ?__(2,"Pick point")};
100      (1,_) -> {body,{explode,Axis}};
101      (3,_) -> {body,{explode,{Axis,{'ASK',[point]}}}};
102      (_,_) -> ignore
103    end,
104    {AxisStr,{explode,Fun}}.
105
106radial_axis() ->
107     [radial_menu(x),
108      radial_menu(y),
109      radial_menu(z),
110      separator,
111      radial_menu(last_axis),
112      radial_menu(default_axis)].
113
114radial_menu(Axis) ->
115    AxisStr = wings_util:cap(wings_s:dir({radial,Axis})),
116    Fun = fun
117      (help,_) ->
118        Help0 = ?__(1,"Explode objects along radial of ~s axis"),
119        Help = wings_util:format(Help0, [AxisStr]),
120        {Help,[],?__(2,"Pick  point")};
121      (1,_) -> {body,{explode_radial,Axis}};
122      (3,_) -> {body,{explode_radial,{Axis,{'ASK',[point]}}}};
123      (_,_) -> ignore
124    end,
125    {AxisStr,{explode_radial,Fun}}.
126
127%%%
128%%% Commands
129%%%
130
131%% Explode Axis commands
132command({body,{explode,{Axis,{'ASK',Ask}}}}, St) ->
133    wings:ask({Ask,[]}, St, fun (Res,St0) ->
134      explode({Axis,Res}, St0)
135    end);
136command({body,{explode,{'ASK',Ask}}}, St) ->
137    wings:ask({Ask,[]}, St, fun
138      ({_,_}=Res,St0) -> explode(Res, St0);
139      (Axis,St0)->
140        Center = wings_sel:center(St0),
141        explode({Axis,Center}, St0)
142    end);
143command({body,{explode,{Axis,Point}}}, St) ->
144    explode({Axis,Point}, St);
145command({body,{explode,Axis}}, St) ->
146    Center = wings_sel:center(St),
147    explode({Axis,Center}, St);
148%% Explode Radial commands
149command({body,{explode_radial,{Axis,{'ASK',Ask}}}}, St) ->
150    wings:ask({Ask,[]}, St, fun (Res,St0) ->
151      explode_radial({Axis,Res}, St0)
152    end);
153command({body,{explode_radial,{'ASK',Ask}}}, St) ->
154    wings:ask({Ask,[]}, St, fun
155      ({_,_}=Res,St0) ->
156        explode_radial(Res, St0);
157      (Axis,St0)->
158        Center = wings_sel:center(St0),
159        explode_radial({Axis,Center}, St0)
160    end);
161command({body,{explode_radial,{Axis,Point}}}, St) ->
162    explode_radial({Axis,Point}, St);
163command({body,{explode_radial,Axis}}, St) ->
164    Center = wings_sel:center(St),
165    explode_radial({Axis,Center}, St);
166%% Explode Uniform commands
167command({body,{explode_uniform,{'ASK',Ask}}}, St) ->
168    wings:ask({Ask,[]}, St, fun (Point,St0) ->
169      explode_uniform(Point, St0)
170    end);
171command({body,{explode_uniform,Point}}, St) ->
172    explode_uniform(Point, St);
173command({body,explode_uniform}, St) ->
174    Center = wings_sel:center(St),
175    explode_uniform(Center, St);
176command(_,_) -> next.
177
178%%%
179%%% Process commands
180%%%
181
182%% Explode
183explode({Axis0,Point}, St) ->
184    Axis = wings_util:make_vector(Axis0),
185    wings_drag:matrix(
186      fun(We) ->
187              Center = wings_vertex:center(We),
188              explode_fun(Axis, Point, Center)
189      end, [percent], St).
190
191%% Explode Uniform
192explode_uniform(Point, St) ->
193    wings_drag:matrix(
194      fun(We) ->
195              Center = wings_vertex:center(We),
196              explode_uniform_fun(Point, Center)
197      end, [percent], St).
198
199%% Explode Radial
200explode_radial({Axis0,Point}, St) ->
201    Axis = wings_util:make_vector(Axis0),
202    wings_drag:matrix(
203      fun(We) ->
204              Center = wings_vertex:center(We),
205              explode_radial_fun(Axis, Point, Center)
206      end, [percent], St).
207
208
209%%%
210%%% Explode fun
211%%%
212
213explode_fun(Axis, Point, Center) ->
214    Dist = dist_along_vector(Center, Point, Axis),
215    fun(Matrix, [Percent]) ->
216            ScaledAxis = e3d_vec:mul(Axis, Percent * Dist),
217            TransVec = e3d_mat:mul_point(Matrix, ScaledAxis),
218            e3d_mat:translate(TransVec)
219    end.
220
221explode_uniform_fun(Point, Center) ->
222    Axis = e3d_vec:norm_sub(Center, Point),
223    Dist = e3d_vec:dist(Center, Point),
224    fun(Matrix, [Percent]) ->
225            ScaledAxis = e3d_vec:mul(Axis, Percent * Dist),
226            TransVec = e3d_mat:mul_point(Matrix, ScaledAxis),
227            e3d_mat:translate(TransVec)
228    end.
229
230explode_radial_fun(Axis, Point, Center) ->
231    Vec = e3d_vec:sub(Point, Center),
232    Cross = e3d_vec:cross(Vec, Axis),
233    Radial = e3d_vec:norm(e3d_vec:cross(Cross, Axis)),
234    Dist = dist_along_vector(Center, Point, Radial),
235    fun(Matrix, [Percent]) ->
236            ScaledRadial = e3d_vec:mul(Radial, Percent * Dist),
237            TransVec = e3d_mat:mul_point(Matrix, ScaledRadial),
238            e3d_mat:translate(TransVec)
239    end.
240
241%%%
242%%% Utilities
243%%%
244
245dist_along_vector({Xa,Ya,Za},{Xb,Yb,Zb},{Vx,Vy,Vz}) ->
246%% Return Distance between PosA and PosB along Normalized Vector
247    Vx*(Xa-Xb)+Vy*(Ya-Yb)+Vz*(Za-Zb).
248