1%%
2%%  wings_align.erl --
3%%
4%%     This module contains the Align and Center commands.
5%%
6%%  Copyright (c) 2001-2011 Bjorn Gustavsson
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%%     $Id$
12%%
13
14-module(wings_align).
15-export([align/2,center/2,copy_bb/1,
16	 scale_to_bb/2,scale_to_bb_prop/2,move_to_bb/2,move_bb_to_sel/2,
17	 scale_bb_to_sel/2,put_on_ground/1,unitize/1]).
18
19-include("wings.hrl").
20-import(lists, [map/2,foldl/3,reverse/1]).
21
22align(_Axis, #st{sel=[]}=St) ->
23    St;
24align(Axis, #st{selmode=Mode}=St0) ->
25    CF = fun(Items, We) ->
26                 Vs = wings_sel:to_vertices(Mode, Items, We),
27                 C = e3d_vec:average(wings_vertex:bounding_box(Vs, We)),
28                 We#we{temp=C}
29         end,
30    St = wings_sel:map(CF, St0),
31    MF = fun(_, #we{temp=C}) -> [C] end,
32    RF = fun erlang:'++'/2,
33    Center = e3d_vec:average(wings_sel:dfold(MF, RF, [], St)),
34    move_to(Center, Axis, St).
35
36center(_Axis, #st{sel=[]}=St) ->
37    St;
38center(Axis, St0) ->
39    MF = fun(_, We) ->
40                 [wings_we:centroid(We)]
41         end,
42    RF = fun erlang:'++'/2,
43    Center = e3d_vec:average(wings_sel:dfold(MF, RF, [], St0)),
44    CF = fun(_, We) -> We#we{temp=Center} end,
45    St = wings_sel:map(CF, St0),
46    move_to(e3d_vec:zero(), Axis, St).
47
48copy_bb(St) ->
49    BB = wings_sel:bounding_box(St),
50    St#st{bb=BB}.
51
52scale_to_bb(_Dir, #st{bb=none}=St) -> St;
53scale_to_bb(Dir, #st{bb=Dest}=St) ->
54    case wings_sel:bounding_box(St) of
55	none -> St;
56	Src ->
57	    Center = wings_sel:center(St),
58	    Matrix = make_scale(Dir, Src, Dest, Center),
59	    transform(Matrix, St)
60    end.
61
62scale_to_bb_prop(_Dir, #st{bb=none}=St) -> St;
63scale_to_bb_prop(Dir, #st{bb=Dest}=St) ->
64    case wings_sel:bounding_box(St) of
65	none -> St;
66	Src ->
67	    Center = wings_sel:center(St),
68	    Matrix = make_prop_scale(Dir, Src, Dest, Center),
69	    transform(Matrix, St)
70    end.
71
72move_to_bb(_Dir, #st{bb=none}=St) -> St;
73move_to_bb(Dir, #st{bb=Dest}=St) ->
74    case wings_sel:bounding_box(St) of
75	none -> St;
76	Src ->
77	    Matrix = make_move(Dir, Src, Dest),
78	    transform(Matrix, St)
79    end.
80
81move_bb_to_sel(_Dir, #st{sel=[]}=St) -> St;
82move_bb_to_sel(Dir, #st{bb=[P1,P2]=BB}=St) ->
83    CurrentCenter = e3d_vec:average(BB),
84    Vec1 = e3d_vec:sub(CurrentCenter,P1),
85    Vec2 = e3d_vec:sub(CurrentCenter,P2),
86    SelC = filter_coord(Dir, e3d_vec:sub(wings_sel:bbox_center(St),CurrentCenter)),
87    SelCenter = e3d_vec:add(CurrentCenter,SelC),
88    BB1 = e3d_vec:add(SelCenter, Vec1),
89    BB2 = e3d_vec:add(SelCenter, Vec2),
90    St#st{bb=[BB1,BB2]};
91move_bb_to_sel(_Dir, St) -> St.
92
93scale_bb_to_sel(_Dir, #st{sel=[]}=St) -> St;
94scale_bb_to_sel(Dir, #st{bb=[A1,A2]}=St) ->
95    [B1,B2] = wings_sel:bounding_box(St),
96    {B11x,B11y,B11z} = filter_coord2(Dir,B1),
97    {B21x,B21y,B21z} = filter_coord2(Dir,B2),
98    {A11x,A11y,A11z} = A1,
99    {A21x,A21y,A21z} = A2,
100    Point1 = {if B11x =:= none -> A11x; true -> B11x end,
101              if B11y =:= none -> A11y; true -> B11y end,
102              if B11z =:= none -> A11z; true -> B11z end},
103    Point2 = {if B21x =:= none -> A21x; true -> B21x end,
104              if B21y =:= none -> A21y; true -> B21y end,
105              if B21z =:= none -> A21z; true -> B21z end},
106    BBC1 = e3d_vec:average(Point1,Point2),
107    BBC2 = e3d_vec:average(A1,A2),
108    TransVec = e3d_vec:sub(BBC2,BBC1),
109    BB1 = e3d_vec:add(TransVec,Point1),
110    BB2 = e3d_vec:add(TransVec,Point2),
111    St#st{bb=[BB1,BB2]};
112scale_bb_to_sel(_Dir, St) -> St.
113
114transform(Matrix, St) ->
115    wings_sel:map(fun(_Items, We0) ->
116			  wings_we:transform_vs(Matrix, We0)
117		  end, St).
118
119make_move(Dir, Src, Dest0) ->
120    SrcMid = e3d_vec:average(Src),
121    DestMid = e3d_vec:average(Dest0),
122    Tvec = filter_coord(Dir, e3d_vec:sub(DestMid, SrcMid)),
123    e3d_mat:translate(Tvec).
124
125make_scale(Dir, Src0, Dest0, Center) ->
126    Pre = e3d_mat:translate(Center),
127    Post = e3d_mat:translate(e3d_vec:neg(Center)),
128    Src1 = e3d_vec:sub(Src0),
129    Dest1 = e3d_vec:sub(Dest0),
130    Src = filter_coord(Dir, Src1),
131    Dest = filter_coord(Dir, Dest1),
132    Sc0 = make_scales(Dest, Src),
133    [ScX,ScY,ScZ] = map(fun(none) -> 1.0;
134			   (Sc) -> Sc end, Sc0),
135    e3d_mat:mul(e3d_mat:mul(Pre,e3d_mat:scale(ScX, ScY, ScZ)),Post).
136
137make_prop_scale(Dir, Src0, Dest0, Center) ->
138    Pre = e3d_mat:translate(Center),
139    Post = e3d_mat:translate(e3d_vec:neg(Center)),
140    Src1 = e3d_vec:sub(Src0),
141    Dest1 = e3d_vec:sub(Dest0),
142    Src = filter_coord(Dir, Src1),
143    Dest = filter_coord(Dir, Dest1),
144    Sc0 = make_scales(Dest, Src),
145    Min = min_scale(Sc0),
146    e3d_mat:mul(e3d_mat:mul(Pre,e3d_mat:scale(Min, Min, Min)),Post).
147
148make_scales(Ta, Tb) ->
149    make_scales(1, Ta, Tb).
150
151make_scales(I, Ta, Tb) when I > tuple_size(Ta); I > tuple_size(Tb) -> [];
152make_scales(I, Ta, Tb) ->
153    S = case {element(I, Ta),element(I, Tb)} of
154	    {_,0.0} -> none;
155	    {A,B} ->
156		case catch A / B of		%catch if B is very small
157		    {'EXIT',_} -> none;
158		    Q -> Q
159		end
160	end,
161    [S|make_scales(I+1, Ta, Tb)].
162
163min_scale([none|Ss]) -> min_scale(Ss);
164min_scale([S|Ss]) -> min_scale(Ss, S).
165
166min_scale([none|Ss], Min) ->
167    min_scale(Ss, Min);
168min_scale([S|Ss], Min) when S < Min ->
169    min_scale(Ss, S);
170min_scale([_|Ss], Min) ->
171    min_scale(Ss, Min);
172min_scale([], Min) -> Min.
173
174move_to(Center, Axis, St) ->
175    MF = fun(_, #we{vp=Vtab0,temp=MyCenter}=We) ->
176                 Offset0 = e3d_vec:sub(Center, MyCenter),
177                 case filter_coord(Axis, Offset0) of
178                     {0.0,0.0,0.0} ->
179                         We#we{temp=[]};
180                     Offset ->
181                         Vtab = offset(Offset, Vtab0),
182                         We#we{vp=Vtab,temp=[]}
183                 end
184         end,
185    wings_sel:map(MF, St).
186
187filter_coord(x, {X,_,_}) -> {X,0.0,0.0};
188filter_coord(y, {_,Y,_}) -> {0.0,Y,0.0};
189filter_coord(z, {_,_,Z}) -> {0.0,0.0,Z};
190filter_coord(radial_x, {_,Y,Z}) -> {0.0,Y,Z};
191filter_coord(radial_y, {X,_,Z}) -> {X,0.0,Z};
192filter_coord(radial_z, {X,Y,_}) -> {X,Y,0.0};
193filter_coord(all, All) -> All.
194
195filter_coord2(x, {X,_,_}) -> {X,none,none};
196filter_coord2(y, {_,Y,_}) -> {none,Y,none};
197filter_coord2(z, {_,_,Z}) -> {none,none,Z};
198filter_coord2(radial_x, {_,Y,Z}) -> {none,Y,Z};
199filter_coord2(radial_y, {X,_,Z}) -> {X,none,Z};
200filter_coord2(radial_z, {X,Y,_}) -> {X,Y,none};
201filter_coord2(all, All) -> All.
202
203
204offset(Offset, Vtab0) ->
205    Vtab = array:sparse_foldl(fun(V, Pos, A) ->
206				      [{V,e3d_vec:add(Pos, Offset)}|A]
207			      end, [], Vtab0),
208    array:from_orddict(reverse(Vtab)).
209
210%% @doc Move selected object(s) vertically until it rests on the ground plane
211%% @spec put_on_ground(St::st#) -> St# ?
212put_on_ground(St) ->
213    wings_sel:map(fun(_, We) -> put_obj_on_ground(We) end, St).
214
215put_obj_on_ground(We) ->
216    [{_,Ymin,_},_] = wings_vertex:bounding_box(We),
217    Matrix = e3d_mat:translate(0.0, -Ymin, 0.0),
218    wings_we:transform_vs(Matrix, We).
219
220%% @doc Scale selected object(s) uniformly until it's bounding box fits
221%% inside a sphere of radius 1.0, and move object to origin
222%% @spec unitize(St::st#) -> St# ?
223unitize(St) ->
224    wings_sel:map(fun(_, We) -> unitize_obj(We) end, St).
225
226unitize_obj(We) when ?IS_LIGHT(We) -> We;
227unitize_obj(We) ->
228    [Min,Max] = wings_vertex:bounding_box(We),
229    Size = e3d_vec:sub(Max, Min),
230    Center = e3d_vec:average(Min, Max),
231    Scale = 2.0 / e3d_vec:len(Size),
232    LocMat = e3d_mat:translate(e3d_vec:neg(Center)),
233    SizMat = e3d_mat:scale(Scale),
234    Matrix = e3d_mat:mul(SizMat, LocMat),
235    wings_we:transform_vs(Matrix, We).
236
237