1%% 2%% wpc_absolute_scale.erl -- 3%% 4%% Plug-in for scale -> absolute 5%% 6%% Copyright (c) 2006-2011 Andrzej Giniewicz 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-module(wpc_absolute_scale). 14 15-include_lib("src/wings.hrl"). 16 17-export([init/0,menu/2,command/2]). 18 19-define(EPSILON,0.0000005). 20 21%%% 22%%% plugin interface 23%%% 24 25init() -> true. 26 27menu({Mode},Menu) when Mode == vertex; Mode == edge; Mode == face; Mode == body -> 28 parse(Menu, Mode); 29menu(_,Menu) -> 30 Menu. 31 32parse(Menu, Mode) -> 33 lists:reverse(parse(Menu, Mode, [], false)). 34 35parse([], _, NewMenu, true) -> 36 NewMenu; 37parse([], Mode, NewMenu, false) -> 38 [draw(all, Mode), separator|NewMenu]; 39parse([{Name, {absolute, Commands}}|Rest], Mode, NewMenu, false) -> 40 parse(Rest, Mode, [{Name, {absolute, Commands++draw(menu, Mode)}}|NewMenu], true); 41parse([separator|Rest], Mode, NewMenu, false) -> 42 parse(Rest, Mode, [separator, draw(all, Mode)|NewMenu], true); 43parse([Elem|Rest], Mode, NewMenu, Found) -> 44 parse(Rest, Mode, [Elem|NewMenu], Found). 45 46draw(all, Mode) -> 47 {?__(1, "Absolute Commands"), {absolute, draw(menu, Mode)}}; 48draw(menu, Mode) -> 49 [{?__(2,"Scale"),scale_fun(Mode), 50 {?__(3,"Scale to exact size in absolute coordinates"), 51 ?__(4,"Scale to target size"), 52 ?__(5,"Scale with center picking")},[]}]. 53 54scale_fun(Mode) -> 55 fun(1, _Ns) -> 56 {Mode,{absolute,scale}}; 57 (2, _Ns) -> 58 {Mode,{absolute,ctscale}}; 59 (3, _Ns) -> 60 {Mode,{absolute,cscale}}; 61 (_, _) -> ignore 62 end. 63 64command({_,{absolute,Mode}},St) when Mode == scale; Mode == ctscale; Mode == cscale -> 65 case Mode of 66 scale -> 67 scale(St); 68 ctscale -> 69 wings:ask(selection_ask([center,target]), St, fun ctscale/2); 70 cscale -> 71 wings:ask(selection_ask([center]), St, fun cscale/2) 72 end; 73command(_,_) -> next. 74 75scale(St) -> 76 Options = extract([], St), 77 draw_window(Options, St). 78 79ctscale({Center, TA, TB}, St) -> 80 Options = extract([{center,Center},{ta,TA},{tb,TB}], St), 81 draw_window(Options, St). 82 83cscale(Center, St) -> 84 Options = extract([{center,Center}], St), 85 draw_window(Options, St). 86 87%% 88%% extract(State) 89%% 90%% functions that extracts all needed information from state 91%% it returns {Options,Selection}, where options is: 92%% {{center,{CX,CY,CZ}},{size,{SX,SY,SZ}},{scalewhole,WholeObject},{oneobject,OneObject}} 93%% (return values: {{atom,{float,float,float}},{atom,{float,float,float}},{atom,atom(always/never/ask)},{atom,bool}}) 94%% 95 96extract(Options, St) -> 97 Center = wings_sel:center_vs(St), 98 WholeObject = check_whole_obj(St), 99 BB = case check_whole_obj(St) of 100 always -> whole_bbox(St); 101 _ -> wings_sel:bounding_box(St) 102 end, 103 Size = bb2size(BB), 104 TA = lookup(ta, Options, {0.0, 0.0, 0.0}), 105 TB = lookup(tb, Options, Size), 106 [{TX1,TY1,TZ1},{TX2,TY2,TZ2}] = TBB = normalizeBB([TA,TB]), 107 NCenter = lookup(center, Options, Center), 108 OneObject = check_single_obj(St), 109 SugSize = {abs(TX1-TX2),abs(TY1-TY2),abs(TZ1-TZ2)}, 110 SugCenter = case lookup(ta, Options, false) of 111 false -> none; 112 _ -> getSuggestedCenter(BB, TBB) 113 end, 114 {{center,NCenter},{suggestcenter,SugCenter},{size,Size}, 115 {suggestsize,SugSize},{scalewhole,WholeObject},{oneobject,OneObject}}. 116 117whole_bbox(St) -> 118 MF = fun(_, We) -> wings_vertex:bounding_box(We) end, 119 RF = fun(W, []) -> W end, 120 wings_sel:dfold(MF, RF, [], St). 121 122lookup(Key, List, Default) -> 123 case lists:keyfind(Key, 1, List) of 124 {_,Value} -> Value; 125 false -> Default 126 end. 127 128normalizeBB([{AX1,AY1,AZ1},{AX2,AY2,AZ2}]) -> 129 [{min(AX1,AX2), min(AY1,AY2), min(AZ1, AZ2)}, 130 {max(AX1,AX2), max(AY1,AY2), max(AZ1, AZ2)}]. 131 132getSuggestedCenter([{AX1,AY1,AZ1},{AX2,AY2,AZ2}], 133 [{BX1,BY1,BZ1},{BX2,BY2,BZ2}]) -> 134 {getC(AX1,AX2,BX1,BX2), 135 getC(AY1,AY2,BY1,BY2), 136 getC(AZ1,AZ2,BZ1,BZ2)}. 137 138getC(A1, A2, B1, B2) when A1 =/= A2, B1 =/= B2, ((A1 - A2)-(B1 - B2))=/=0.0 -> 139 (B1*A2-B2*A1)/((A2-A1) - (B2-B1)); 140getC(A1, A2, _, _) -> (A1+A2)/2. 141 142bb2size([LL,UR]) -> 143 e3d_vec:sub(UR, LL). 144 145check_whole_obj(#st{selmode=Mode}=St0) -> 146 MF = fun(Items, We) -> 147 Vs = wings_sel:to_vertices(Mode, Items, We), 148 case {Vs,wings_we:visible_vs(We)} of 149 {[_],_} -> always; 150 {Vs,Vs} -> never; 151 {_,_} -> ask 152 end 153 end, 154 RF = fun(When, never) -> When; 155 (_, _) -> ask 156 end, 157 wings_sel:dfold(MF, RF, never, St0). 158 159check_single_obj(#st{sel=[_]}) -> true; 160check_single_obj(_) -> false. 161 162selection_ask(Asks) -> 163 Ask = selection_ask(Asks,[]), 164 {Ask,[],[],[vertex, edge, face, body]}. 165 166selection_ask([],Ask) -> lists:reverse(Ask); 167selection_ask([center|Rest], Ask) -> 168 Desc = ?__(1,"Select scale center"), 169 selection_ask(Rest, [{point,Desc}|Ask]); 170selection_ask([target|Rest], Ask) -> 171 Desc1 = ?__(2,"Select target size - base point"), 172 Desc2 = ?__(3,"Select target size - range point"), 173 selection_ask(Rest, [{point,Desc2},{point,Desc1}|Ask]). 174 175%% 176%% draw_window(Options,Selection,State) 177%% 178%% functions that draws interface and translates entered options for further processing 179%% and calls do_scale(ProcessedOptions,Selection,State) 180%% 181 182draw_window({{_,{CX,CY,CZ}}, {_, SugCenter}, {_,{SX,SY,SZ}=Size}, {_, {SugX, SugY, SugZ}}, {_,Whole}, {_,Single}}, St) -> 183 Frame1 = [{hframe, 184 [draw_window1(size, {{SX,SY,SZ},{SugX,SugY,SugZ}}), 185 draw_window1(aspect, {SX,SY,SZ}), 186 draw_window1(center, {CX,CY,CZ})]}], 187 Frame2 = if 188 Whole == ask -> [draw_window1(whole, Single)]; 189 true -> [] 190 end, 191 Frame3 = if 192 SugCenter == none -> []; 193 true -> [draw_window1(sugc, true)] 194 end, 195 Frame = [{vframe, Frame1 ++ Frame2 ++ Frame3}], 196 Name = draw_window1(name,default), 197 F = fun({dialog_preview,Scale}) -> 198 {preview,St,translate(Scale,SugCenter, Size, St)}; 199 (cancel) -> 200 St; 201 (Scale) -> 202 {commit,St,translate(Scale, SugCenter, Size, St)} 203 end, 204 wings_dialog:dialog(Name, {preview,Frame}, F). 205 206draw_window1(name,_) -> 207 ?__(1,"Absolute scale options"); 208draw_window1(size, {{X,Y,Z},{SugX,SugY,SugZ}}) -> 209 {vframe,[ 210 {hframe,[{label,?__(2,"Set new size")++":"}]}, 211 {label_column,[ 212 {"X:",{text,SugX,[{key,sx},disable(X==0.0)]}}, 213 {"Y:",{text,SugY,[{key,sy},disable(Y==0.0)]}}, 214 {"Z:",{text,SugZ,[{key,sz},disable(Z==0.0)]}} 215 ]} 216 ]}; 217draw_window1(center,{X,Y,Z}) -> 218 {vframe,[ 219 {hframe,[{label,?__(3,"Set scale center")++":"}]}, 220 {label_column,[ 221 {"X:",{text,X,[{key,cx}]}}, 222 {"Y:",{text,Y,[{key,cy}]}}, 223 {"Z:",{text,Z,[{key,cz}]}} 224 ]} 225 ]}; 226draw_window1(aspect,{X,Y,Z}) -> 227 {vframe,[ 228 {hframe,[{label," " ++ ?__(6,"Link")++":"}]}, 229 {label_column,[ 230 {" ",{hframe,[{"",false,[{key,ax},disable(X==0.0)]}],[{border, 3}]}}, 231 {" ",{hframe,[{"",false,[{key,ay},disable(Y==0.0)]}],[{border, 3}]}}, 232 {" ",{hframe,[{"",false,[{key,az},disable(Z==0.0)]}],[{border, 3}]}} 233 ]} 234 ]}; 235draw_window1(whole,true) -> 236 {?__(4,"Scale whole object"),false,[{key,whole}]}; 237draw_window1(whole,false) -> 238 {?__(5,"Scale whole objects"),false,[{key,whole}]}; 239draw_window1(sugc,_) -> 240 {?__(7,"Fit selection to target"),false, 241 [{key,sugc}, {hook, fun disable/3}]}. 242 243disable(sugc, Bool, Store) -> 244 _ = [wings_dialog:enable(Key, not Bool, Store) || Key <- [cx,cy,cz]]. 245 246disable(Bool) -> 247 {hook, fun(Key, _, Store) -> 248 wings_dialog:enable(Key, not Bool, Store) 249 end}. 250 251checkChained(Data) -> 252 [{_,Factor}|_]=lists:keysort(1,checkChained(Data, [{0.0,1.0}])), 253 Factor. 254checkChained([], List) -> List; 255checkChained([{SX,OX,true}|Rest], List) -> 256 checkChained(Rest, [{-abs(SX/OX-1.0),SX/OX}|List]); 257checkChained([_|Rest], List) -> 258 checkChained(Rest, List). 259 260translate(Options, SugCenter, {OX,OY,OZ}=Original, St) -> 261 SX = lookup(sx, Options, OX), 262 SY = lookup(sy, Options, OY), 263 SZ = lookup(sz, Options, OZ), 264 CX = lookup(cx, Options, 0.0), 265 CY = lookup(cy, Options, 0.0), 266 CZ = lookup(cz, Options, 0.0), 267 AX = lookup(ax, Options, false), 268 AY = lookup(ay, Options, false), 269 AZ = lookup(az, Options, false), 270 Whole = lookup(whole, Options, true), 271 SugC = lookup(sugc, Options, false), 272 ChainedFactor = checkChained([{SX,OX,AX},{SY,OY,AY},{SZ,OZ,AZ}]), 273 NX = if 274 AX -> OX*ChainedFactor; 275 true -> SX 276 end, 277 NY = if 278 AY -> OY*ChainedFactor; 279 true -> SY 280 end, 281 NZ = if 282 AZ -> OZ*ChainedFactor; 283 true -> SZ 284 end, 285 Center = if 286 SugC -> SugCenter; 287 true -> {CX,CY,CZ} 288 end, 289 do_scale([{NX,NY,NZ},Original,Center,{whole,Whole}], St). 290 291%% 292%% do_scale(Options,Selection,State) 293%% 294%% this is main absolute scale command, it returns new state. 295%% 296 297do_scale([{SX,SY,SZ},{OX,OY,OZ},{CX,CY,CZ},{_,Whole}], St) -> 298 SX2 = if 299 OX == 0.0 -> 1.0; 300 Whole andalso (OX =< ?EPSILON) andalso (SX =< ?EPSILON) -> 1.0; 301 true -> SX/OX 302 end, 303 SY2 = if 304 OY == 0.0 -> 1.0; 305 Whole andalso (OY =< ?EPSILON) andalso (SY =< ?EPSILON) -> 1.0; 306 true -> SY/OY 307 end, 308 SZ2 = if 309 OZ == 0.0 -> 1.0; 310 Whole andalso (OZ =< ?EPSILON) andalso SZ =< ?EPSILON -> 1.0; 311 true -> SZ/OZ 312 end, 313 Pre = e3d_mat:translate(CX, CY, CZ), 314 Scale = e3d_mat:scale(SX2, SY2, SZ2), 315 Post = e3d_mat:translate(-CX, -CY, -CZ), 316 Mat = e3d_mat:mul(e3d_mat:mul(Pre, Scale), Post), 317 if 318 Whole -> 319 MF = fun(_, We) -> 320 wings_we:transform_vs(Mat, We) 321 end, 322 wings_sel:map(MF, St); 323 true -> 324 do_scale_1(Mat, St) 325 end. 326 327do_scale_1(Mat, #st{selmode=Mode}=St) -> 328 MF = fun(Items, #we{vp=Vtab0}=We) -> 329 Vs0 = wings_sel:to_vertices(Mode, Items, We), 330 Vs = gb_sets:from_list(Vs0), 331 Vtab = execute_scale(Mat, Vs, Vtab0), 332 We#we{vp=Vtab} 333 end, 334 wings_sel:map(MF, St). 335 336execute_scale(Mat, Vset, Vtab) -> 337 execute_scale(array:sparse_size(Vtab)-1, Mat, Vset, Vtab). 338 339execute_scale(-1, _, _, Vtab) -> 340 Vtab; 341execute_scale(Vertex, Mat, Vset, Vtab0) -> 342 case array:get(Vertex, Vtab0) of 343 undefined -> 344 execute_scale(Vertex-1, Mat, Vset, Vtab0); 345 Pos0 -> 346 Vtab = case gb_sets:is_element(Vertex, Vset) of 347 true -> 348 Pos = e3d_mat:mul_point(Mat, Pos0), 349 array:set(Vertex, Pos, Vtab0); 350 false -> 351 Vtab0 352 end, 353 execute_scale(Vertex-1, Mat, Vset, Vtab) 354 end. 355