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