1%% 2%% wpc_constraints.erl -- 3%% 4%% Plugin for setting default constraints directly from a model 5%% 6%% Copyright (c) 2008-2013 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%% $Id: wpc_constraints.erl optigon Exp $ 12%% 13 14-module(wpc_constraints). 15-export([init/0,menu/2,command/2]). 16-define(NEED_ESDL, 1). 17-include_lib("src/wings.hrl"). 18-include_lib("e3d/e3d.hrl"). 19-define(NONZERO, 1.0e-6). 20 21init() -> 22 true. 23 24menu({Mode},Menu) when Mode == vertex; Mode == edge; Mode == face -> 25 lists:reverse(parse(Menu,Mode,[],false)); 26menu(_,Menu) -> Menu. 27 28 29parse([], _, NewMenu, true) -> 30 NewMenu; 31 32parse([], Mode, NewMenu, false) -> 33 [general_menu(Mode), separator|NewMenu]; 34 35parse([A={_,vertex_color,_}|Rest], Mode, NewMenu, false) -> 36 parse(Rest, Mode, [general_menu(Mode),separator,A|NewMenu], true); 37 38parse([Elem|Rest], Mode, NewMenu, Found) -> 39 parse(Rest, Mode, [Elem|NewMenu], Found). 40 41 42general_menu(Mode) -> 43 MenuTitle = ?__(1,"Set Constraint"), 44 {MenuTitle,{set_constraint,first_menu(Mode)}}. 45 46%%% Menu 47first_menu(vertex) -> 48 [general_menu1(vertex,center)]; 49 50first_menu(edge) -> 51 [general_menu1(edge,total), 52 general_menu1(edge,average), 53 separator, 54 general_menu1(edge,angle), 55 general_menu1(edge,sub_angle), 56 general_menu1(edge,to_axis), 57 separator, 58 general_menu1(edge,scale), 59 general_menu1(edge,diff), 60 general_menu1(edge,center)]; 61 62first_menu(face) -> 63 [general_menu1(face,center), 64 general_menu1(face,scale), 65 separator, 66 general_menu1(face,angle), 67 general_menu1(face,sub_angle), 68 general_menu1(face,to_axis)]. 69 70general_menu1(Mode,Type) -> 71 {menu_title(Mode,Type),{Type,menu_items1(Mode,Type)},menu_string1(Mode,Type)}. 72 73general_menu2(Mode,Type,Axis) -> 74 RmbStr = ?__(1,"Choose a different axis to measure the second selection"), 75 F = fun(help, _Ns) -> 76 {menu_string2(Mode,Type,Axis)++mod_string(),[],RmbStr}; 77 (1,_Ns) -> {Mode,{set_constraint,{Type,{Axis,none}}}}; 78 (2,_Ns) -> ignore; 79 (3,_Ns) -> menu_items2(Mode,Type,Axis) 80 end, 81 {menu_heading(Mode,Type,Axis),{Axis,F},[]}. 82 83general_menu3(Mode,Type,Axis1,Axis2) -> 84 Str = menu_string3(advanced,Mode,Type,Axis1,Axis2), 85 F = {Mode,{set_constraint,{Type,{Axis1,Axis2}}}}, 86 {menu_heading(Mode,Type,Axis2),{Axis2,F},Str++mod_string()}. 87 88%%% Menu Strings 89menu_title(Mode,Type) -> 90 case {Mode,Type} of 91 {Mode,total} -> ?__(1,"Total Length"); 92 {Mode,average} -> ?__(2,"Average Length"); 93 {Mode,angle} -> ?__(3,"Angle"); 94 {Mode,sub_angle} -> ?__(4,"Subtract Angle"); 95 {edge,to_axis} -> ?__(5,"Edge To Axis"); 96 {face,to_axis} -> ?__(6,"Face To Axis"); 97 {Mode,scale} -> ?__(7,"Percentage"); 98 {Mode,diff} -> ?__(8,"Difference"); 99 {Mode,center} -> ?__(9,"Centers") 100 end. 101 102menu_heading(Mode,Type,Axis) -> 103 case {Mode,Type,Axis} of 104 {Mode,angle,normal} -> ?__(4,"Angle"); 105 {Mode,center,normal} -> ?__(3,"Direct"); 106 {Mode,Type,normal} -> ?__(1,"Normal"); 107 {Mode,Type,'ASK'} -> ?__(2,"Pick"); 108 {Mode,sub_angle,fifteen} -> [?__(5,"15"),?DEGREE]; 109 {Mode,sub_angle,twenty_two} -> [?__(6,"22.5"),?DEGREE]; 110 {Mode,sub_angle,thirty} -> [?__(7,"30"),?DEGREE]; 111 {Mode,sub_angle,forty_five} -> [?__(8,"45"),?DEGREE]; 112 {Mode,sub_angle,sixty} -> [?__(9,"60"),?DEGREE]; 113 {Mode,sub_angle,ninety} -> [?__(10,"90"),?DEGREE]; 114 {Mode,Type,Axis} -> wings_s:dir(Axis) 115 end. 116mode_strings(Ending,Mode) -> 117 case {Ending,Mode} of 118 {plural,vertex} -> ?__(1,"vertices"); 119 {plural,edge} -> ?__(2,"edges"); 120 {plural,face} -> ?__(3,"faces"); 121 {singular,vertex} -> ?__(4,"vertex"); 122 {singular,edge} -> ?__(5,"edge"); 123 {singular,face} -> ?__(6,"face"); 124 {second,Mode} -> ?__(8,"second"); 125 {units,vertex} -> ?__(9,"distance"); 126 {units,edge} -> ?__(10,"lengths"); 127 {units,face} -> ?__(11,"areas") 128 end. 129 130mod_string() -> 131 ?__(1,". Constraint bound to held modifier key(s)."). 132 133%%%% Help Strings 134menu_string1(Mode,Type) -> 135 UnitStr = mode_strings(units,Mode), 136 SMdeStr = mode_strings(singular,Mode), 137 PMdeStr = mode_strings(plural,Mode), 138 case {Mode,Type} of 139 {edge,total} -> 140 ?__(1,"Total the lengths of the selected edges and save the result as a distance constraint in the Preferences"); 141 {edge,average} -> 142 ?__(2,"Calculate the average length of the selected edges and save the result as a distance constraint in the Preferences"); 143 {Mode,angle} -> 144 Str = ?__(3,"Calculate the angle between any two ~s and save the result as a rotation constraint in the Preferences"), 145 wings_util:format(Str,[PMdeStr]); 146 {Mode,sub_angle} -> 147 ?__(4,"Calculate the difference between two angles and save the result as a rotation constraint in the Preferences"); 148 {Mode,to_axis} -> 149 Str = ?__(5,"Measure the angle between a single ~s and a standard axis or defined vector. Save the result as a rotation constraint in the Preferences"), 150 wings_util:format(Str,[SMdeStr]); 151 {Mode,scale} -> 152 Str = ?__(6,"Calculate the difference in scale between the ~s of two ~s selections and save the result as a scale constraint in the Preferences"), 153 wings_util:format(Str,[UnitStr,SMdeStr]); 154 {Mode,diff} -> 155 Str = ?__(7,"Calculate the difference in ~s between two ~s selections and save the result as a distance constraint in the Preferences"), 156 wings_util:format(Str,[UnitStr,SMdeStr]); 157 {Mode,center} -> 158 ?__(8,"Calculate the distance between the center points of two selections and save the result as a distance constraint in the Preferences") 159 end. 160 161menu_string2(Mode,Type,Axis) -> 162 UnitStr = mode_strings(units,Mode), 163 SMdeStr = mode_strings(singular,Mode), 164 PMdeStr = mode_strings(plural,Mode), 165 AxisStr = menu_heading(edge,Type,Axis), 166 case {Mode,Type,Axis} of 167 {Mode,scale,normal} -> 168 Str = ?__(1,"Measure the ~s of both selections according to their ~s normals"), 169 wings_util:format(Str,[UnitStr,SMdeStr]); 170 {Mode,scale,'ASK'} -> 171 Str = ?__(2,"Pick an axis along which to measure the ~s both selections"), 172 wings_util:format(Str,[UnitStr]); 173 {Mode,scale,Axis} -> 174 Str = ?__(3,"Measure the ~s of both selections' ~s only along the ~s axis"), 175 wings_util:format(Str,[UnitStr,PMdeStr,AxisStr]); 176 {Mode,diff,normal} -> 177 Str = ?__(1,"Measure the ~s of both selections according to their ~s normals"), 178 wings_util:format(Str,[UnitStr,SMdeStr]); 179 {Mode,diff,'ASK'} -> 180 Str = ?__(2,"Pick an axis along which to measure the ~s both selections"), 181 wings_util:format(Str,[UnitStr]); 182 {Mode,diff,Axis} -> 183 Str = ?__(3,"Measure the ~s of both selections' ~s only along the ~s axis"), 184 wings_util:format(Str,[UnitStr,PMdeStr,AxisStr]) 185 end. 186 187menu_string3(advanced,Mode,Type,Axis1,Axis2) -> 188 UnitStr = mode_strings(units,Mode), 189 PMdeStr = mode_strings(plural,Mode), 190 SMdeStr = mode_strings(singular,Mode), 191 SSelStr = mode_strings(second,Mode), 192 Axs2Str = menu_heading(Mode,Type,Axis2), 193 DegStr = menu_heading(Mode,sub_angle,Axis2), 194 case {Mode,Type,Axis1,Axis2} of 195 {Mode,total,none,normal} -> 196 ?__(1,"Measure the selected edges along their normals"); 197 {Mode,total,none,'ASK'} -> 198 ?__(2,"Pick an axis along which to measure the selected edges"); 199 {Mode,total,none,Axis2} -> 200 Str = ?__(3,"Measure the selected edges only along the ~s axis"), 201 wings_util:format(Str,[Axs2Str]); 202 203 {Mode,average,none,normal} -> 204 ?__(1,"Measure the selected edges along their normals")++ 205 ?__(22," and then calculate their average length"); 206 {Mode,average,none,'ASK'} -> 207 ?__(2,"Pick an axis along which to measure the selected edges")++ 208 ?__(22," and then calculate their average length"); 209 {Mode,average,none,Axis2} -> 210 Str = ?__(3,"Measure the selected edges only along the ~s axis"), 211 wings_util:format(Str,[Axs2Str])++?__(22," and then calculate their average length"); 212 213 {Mode,angle,none,normal} -> 214 ?__(4,"Measure the selected angle"); 215 {Mode,angle,none,'ASK'} -> 216 ?__(5,"Specify the axis from which to measure the selected angle"); 217 {Mode,angle,none,Axis2} -> 218 Str = ?__(6,"Measure the selected angle as viewed from the ~s axis"), 219 wings_util:format(Str,[Axs2Str]); 220 221 {Mode,sub_angle,none,'ASK'} -> 222 ?__(19,"Pick a second angle from which to subtract the currently selected angle"); 223 {Mode,sub_angle,none,Axis2} -> 224 Str = ?__(18,"Subtract the currently selected angle from ~s"), 225 wings_util:format(Str,[DegStr]); 226 227 {Mode,to_axis,none,'ASK'} -> 228 Str = ?__(20,"Pick a vector and calculate the angle to the original ~s"), 229 wings_util:format(Str,[SMdeStr]); 230 {Mode,to_axis,none,Axis2} -> 231 Str = ?__(21,"Calculate the angle from the ~s axis to the original ~s"), 232 wings_util:format(Str,[Axs2Str,SMdeStr]); 233 234 {Mode,center,none,normal} -> 235 ?__(7,"Measure the distance between the centers of the two selections"); 236 {Mode,center,none,'ASK'} -> 237 ?__(8,"Pick an axis along which to measure the distance between the centers of the two selections"); 238 {Mode,center,none,Axis2} -> 239 Str = ?__(9,"Measure only the distance along the ~s axis between the centers of the two selections"), 240 wings_util:format(Str,[Axs2Str]); 241 {Mode,Type,Axis1,normal} -> 242 Str = ?__(13,"Measure the ~s of the ~s selection's ~s along their normals"), 243 wings_util:format(Str,[UnitStr,SSelStr,PMdeStr]); 244 {Mode,Type,Axis1,'ASK'} -> 245 Str = ?__(11,"Pick an axis along which to measure the ~s of the ~s selection's ~s"), 246 wings_util:format(Str,[UnitStr,SSelStr,PMdeStr]); 247 {Mode,Type,Axis1,Axis2} -> 248 Str = ?__(12,"Measure the ~s of the ~s selection's ~s only along the ~s axis"), 249 wings_util:format(Str,[UnitStr,SSelStr,PMdeStr,Axs2Str]) 250 end. 251 252%%% Menu items 253menu_items1(Mode,Type) -> 254 case Type of 255 total -> last_menu(Mode,Type,none); 256 average -> last_menu(Mode,Type,none); 257 angle -> last_menu(Mode,Type,none); 258 sub_angle -> last_menu(Mode,Type,none); 259 to_axis -> last_menu(Mode,Type,none); 260 scale -> middle_menu(Mode,Type); 261 diff -> middle_menu(Mode,Type); 262 center ->last_menu(Mode,Type,none) 263 end. 264 265menu_items2(Mode,Type,Axis) -> 266 case Type of 267 scale -> last_menu(Mode,Type,Axis); 268 diff -> last_menu(Mode,Type,Axis) 269 end. 270 271middle_menu(Mode,Type) -> 272 case Type of 273 Type -> 274 [general_menu2(Mode,Type,normal), 275 general_menu2(Mode,Type,x), 276 general_menu2(Mode,Type,y), 277 general_menu2(Mode,Type,z), 278 general_menu2(Mode,Type,'ASK')] 279 end. 280 281last_menu(Mode,Type,Axis) -> 282 case Type of 283 sub_angle -> 284 [general_menu3(Mode,sub_angle,none,fifteen), 285 general_menu3(Mode,sub_angle,none,twenty_two), 286 general_menu3(Mode,sub_angle,none,thirty), 287 general_menu3(Mode,sub_angle,none,forty_five), 288 general_menu3(Mode,sub_angle,none,sixty), 289 general_menu3(Mode,sub_angle,none,ninety), 290 general_menu3(Mode,sub_angle,none,'ASK')]; 291 to_axis -> 292 [general_menu3(Mode,Type,none,x), 293 general_menu3(Mode,Type,none,y), 294 general_menu3(Mode,Type,none,z), 295 general_menu3(Mode,Type,none,'ASK')]; 296 Type -> 297 [general_menu3(Mode,Type,Axis,normal), 298 general_menu3(Mode,Type,Axis,x), 299 general_menu3(Mode,Type,Axis,y), 300 general_menu3(Mode,Type,Axis,z), 301 general_menu3(Mode,Type,Axis,'ASK')] 302 end. 303 304%%% 305%%% Commands 306%%% 307 308%%% Length 309command({edge,{set_constraint,{total,{none,'ASK'}}}},St) -> 310 wings:ask(selection_ask([along_axis]), St, fun distance/2); 311command({edge,{set_constraint,{total,{none,Axis}}}}, St) -> 312 distance(Axis,St); 313 314%%% Average 315command({edge,{set_constraint,{average,{none,'ASK'}}}},St) -> 316 wings:ask(selection_ask([along_axis]), St, fun average/2); 317command({edge,{set_constraint,{average,{none,Axis}}}}, St) -> 318 average(Axis,St); 319 320%%% Angle 321command({_Mode,{set_constraint,{angle,{none,'ASK'}}}},St) -> 322 check_angle_sel(St), 323 wings:ask(selection_ask([view_plane]), St, fun angle/2); 324command({_Mode,{set_constraint,{angle,{none,Axis}}}}, St) -> 325 check_angle_sel(St), 326 angle(Axis,St); 327 328%%% Sub Angle 329command({_Mode,{set_constraint,{sub_angle,{none,'ASK'}}}},St) -> 330 check_angle_sel(St), 331 wings:ask(secondary_sel_ask(sub_angle,none,none,St), St, fun sub_angle/2); 332command({_Mode,{set_constraint,{sub_angle,{none,Axis}}}}, St) -> 333 check_angle_sel(St), 334 sub_angle(Axis,St); 335 336%%% Angle to Axis 337command({_Mode,{set_constraint,{to_axis,{none,'ASK'}}}},St) -> 338 check_element(St), 339 wings:ask(selection_ask([to_axis]), St, fun to_axis/2); 340command({_Mode,{set_constraint,{to_axis,{none,Axis2}}}},St) -> 341 check_element(St), 342 to_axis(Axis2,St); 343 344%%% Scale Edge Mode 345command({edge,{set_constraint,{scale,{'ASK','ASK'}}}},St) -> 346 wings:ask(selection_ask([along_axis1,along_axis2]), St, 347 fun({Pn0,Pn1},St0) -> 348 scale({Pn0,Pn1},St0) 349 end); 350command({edge,{set_constraint,{scale,{'ASK',none}}}},St) -> 351 wings:ask(selection_ask([along_axis1]), St, fun(Pn1,St0) -> 352 scale({Pn1,Pn1},St0) 353 end); 354command({edge,{set_constraint,{scale,{'ASK',Axis2}}}},St) -> 355 wings:ask(selection_ask([along_axis1]), St, fun({Pn1},St0) -> 356 scale({Pn1,Axis2},St0) 357 end); 358command({edge,{set_constraint,{scale,{Axis1,'ASK'}}}},St) -> 359 wings:ask(selection_ask([along_axis2]), St, fun({Pn1},St0) -> 360 scale({Axis1,Pn1},St0) 361 end); 362command({edge,{set_constraint,{scale,{Axis1,none}}}},St) -> 363 scale({Axis1,Axis1},St); 364command({edge,{set_constraint,{scale,{Axis1,Axis2}}}},St) -> 365 scale({Axis1,Axis2},St); 366command({edge,{set_constraint,{scale,_}}},St) -> 367 wings:ask(selection_ask([along_axis1,along_axis2]), St, 368 fun({Pn0,Pn1},St0) -> 369 scale({Pn0,Pn1},St0) 370 end); 371 372%%% Scale Face Mode 373command({face,{set_constraint,{scale,{'ASK','ASK'}}}},St) -> 374 wings:ask(selection_ask([scale_ax_pnt1,scale_ax_pnt2]), St, 375 fun({Pn0,Pp0,Pn1,Pp1},St0) -> 376 scale({{Pn0,Pp0},{Pn1,Pp1}},St0) 377 end); 378command({face,{set_constraint,{scale,{'ASK',none}}}},St) -> 379 wings:ask(selection_ask([scale_ax_pnt1]), St, fun({Pn1,Pp1},St0) -> 380 scale({{Pn1,Pp1},{Pn1,Pp1}},St0) 381 end); 382command({face,{set_constraint,{scale,{'ASK',Axis2}}}},St) -> 383 wings:ask(selection_ask([scale_ax_pnt1]), St, fun({Pn1,Pp1},St0) -> 384 scale({{Pn1,Pp1},Axis2},St0) 385 end); 386command({face,{set_constraint,{scale,{Axis1,'ASK'}}}},St) -> 387 wings:ask(selection_ask([scale_ax_pnt2]), St, fun({Pn1,Pp1},St0) -> 388 scale({Axis1,{Pn1,Pp1}},St0) 389 end); 390command({face,{set_constraint,{scale,{Axis1,none}}}},St) -> 391 scale({Axis1,Axis1},St); 392command({face,{set_constraint,{scale,{Axis1,Axis2}}}},St) -> 393 scale({Axis1,Axis2},St); 394command({face,{set_constraint,{scale,_}}},St) -> 395 wings:ask(selection_ask([scale_ax_pnt1,scale_ax_pnt2]), St, 396 fun({Pn0,Pp0,Pn1,Pp1},St0) -> 397 scale({{Pn0,Pp0},{Pn1,Pp1}},St0) 398 end); 399 400%%% Difference 401command({edge,{set_constraint,{diff,{'ASK','ASK'}}}},St) -> 402 wings:ask(selection_ask([along_axis1,along_axis2]), St, 403 fun({Axis1,Axis2},St0) -> 404 difference({Axis1,Axis2},St0) 405 end); 406command({edge,{set_constraint,{diff,{'ASK',none}}}},St) -> 407 wings:ask(selection_ask([along_axis1]), St, fun(Axis1,St0) -> 408 difference({Axis1,Axis1},St0) 409 end); 410command({edge,{set_constraint,{diff,{'ASK',Axis2}}}},St) -> 411 wings:ask(selection_ask([along_axis1]), St, fun(Axis1,St0) -> 412 difference({Axis1,Axis2},St0) 413 end); 414command({edge,{set_constraint,{diff,{Axis1,'ASK'}}}},St) -> 415 wings:ask(selection_ask([along_axis2]), St, fun(Axis2,St0) -> 416 difference({Axis1,Axis2},St0) 417 end); 418command({edge,{set_constraint,{diff,{Axis1,none}}}},St) -> 419 difference({Axis1,Axis1},St); 420command({edge,{set_constraint,{diff,{Axis1,Axis2}}}},St) -> 421 difference({Axis1,Axis2},St); 422 423%%% Centers 424command({_Mode,{set_constraint,{center,{none,'ASK'}}}},St) -> 425 wings:ask(selection_ask([centers]), St, fun centers/2); 426command({_Mode,{set_constraint,{center,{none,Axis}}}}, St) -> 427 centers(Axis,St); 428 429command(_,_) -> next. 430 431%%% Axis Asks 432selection_ask(Asks) -> 433 Ask = selection_ask(Asks,[]), 434 {Ask,[],[],[vertex, edge, face]}. 435 436selection_ask([],Ask) -> 437 lists:reverse(Ask); 438 439selection_ask([Type|Rest],Ask) -> 440 {Data,Desc} = case Type of 441 along_axis -> 442 {axis,?__(1,"Choose the axis each edge will be measured along")}; 443 along_axis1 -> 444 {axis,?__(1,"Choose the axis each edge will be measured along") 445 ++?__(2," for the first selection")}; 446 along_axis2 -> 447 {axis,?__(1,"Choose the axis each edge will be measured along") 448 ++?__(3," for the second selection")}; 449 view_plane -> 450 {axis_point,?__(4,"Choose the axis along which the angle will be measured orthographically")}; 451 centers -> 452 {axis,?__(5,"Choose an axis for measuring the distance between the centers of the selections")}; 453 scale_ax_pnt1 -> 454 {axis_point,?__(6,"Choose an axis along which the areas of the first selection will be measure orthographically")}; 455 scale_ax_pnt2 -> 456 {axis_point,?__(7,"Choose an axis along which the areas of the second selection will be measure orthographically")}; 457 to_axis -> 458 {axis,?__(8,"Choose vector to calculate the angle to the to the original selection")} 459 end, 460 selection_ask(Rest,[{Data,Desc}|Ask]). 461 462%%% Secondary selection 463secondary_sel_ask(sub_angle,none,none,OrigSt) -> 464 Desc = ?__(5,"Select an angle made from two edges or two faces to subtract the original angle from"), 465 Data = fun(check, St) -> check_selection(sub_angle,none,none,St,OrigSt); 466 (exit, {_,_,St}) -> 467 case check_selection(sub_angle,none,none,St,OrigSt) of 468 {_," "++_} -> {[],[St]}; 469 {_,_} -> error 470 end 471 end, 472 {[{Data,Desc}],[],[],[edge,face]}; 473secondary_sel_ask(scale_area,Axis1,Axis2,OrigSt) -> 474 Desc = ?__(4,"Select the faces whose total area will be divided into the first selection's area"), 475 Data = fun(check, St) -> check_selection(scale_area,Axis1,Axis2,St,OrigSt); 476 (exit, {_,_,St}) -> 477 case check_selection(scale_area,Axis1,Axis2,St,OrigSt) of 478 {_," "++_} -> {[],[St]}; 479 {_,_} -> error 480 end 481 end, 482 {[{Data,Desc}],[],[],[face]}; 483 484secondary_sel_ask(center,Axis1,Axis2,OrigSt) -> 485 Desc = ?__(3,"Center of selection will determine the distance from the center of the original selection"), 486 Data = fun(check, St) -> check_selection(center,Axis1,Axis2,St,OrigSt); 487 (exit, {_,_,St}) -> 488 case check_selection(center,Axis1,Axis2,St,OrigSt) of 489 {_," "++_} -> {[],[St]}; 490 {_,_} -> error 491 end 492 end, 493 {[{Data,Desc}],[],[],[edge,vertex,face,body]}; 494 495secondary_sel_ask(Type,Axis1,Axis2,OrigSt) -> 496 Desc = case Type of 497 scale -> ?__(1,"Select the edges to divide into the original selection"); 498 difference -> ?__(2,"Select the edges to subtract from the original selection") 499 end, 500 Data = fun(check, St) -> check_selection(Type,Axis1,Axis2,St,OrigSt); 501 (exit, {_,_,St}) -> 502 case check_selection(Type,Axis1,Axis2,St,OrigSt) of 503 {_," "++_} -> {[],[St]}; 504 {_,_} -> error 505 end 506 end, 507 {[{Data,Desc}],[],[],[edge]}. 508 509check_selection(_Type,_Axis1,_Axis2,#st{sel=[]},_OrigSt) -> 510 {none,?__(1,"Nothing selected")}; 511 512check_selection(sub_angle,none,none,#st{sel=[{_Id0,Sel0},{_Id1,Sel1}]}=St,OrigSt) -> 513 case gb_sets:size(Sel0) == 1 andalso gb_sets:size(Sel1) == 1 of 514 true -> 515 OrigA = measure_angle(normal,OrigSt), 516 Angle = measure_angle(normal,St), 517 A0 = abs(Angle - OrigA), 518 A1 = case A0 of 519 0.0 -> 180.0; 520 _ -> A0 521 end, 522 Str = [?__(11," Original Angle ~s"),?DEGREE,?__(12,"\n Current Angle ~s"), 523 ?DEGREE,?__(13,"\n Difference ~s"),?DEGREE], 524 {none,wings_util:format(Str,[OrigA,Angle,A1])}; 525 false -> 526 {none,?__(14,"Select exactly two edges or two faces to define angle")} 527 end; 528 529check_selection(sub_angle,none,none,#st{sel=[{_Id,Sel}]}=St,OrigSt) -> 530 case gb_sets:size(Sel) == 2 of 531 true -> 532 Angle = measure_angle(normal,OrigSt), 533 CAngle = measure_angle(normal,St), 534 OrigA = wings_util:nice_float(Angle), 535 CurrA = wings_util:nice_float(CAngle), 536 A0 = abs(Angle - CAngle), 537 A1 = case A0 of 538 0.0 -> 180.0; 539 _ -> A0 540 end, 541 A2 = wings_util:nice_float(A1), 542 Str1 = [wings_util:format(?__(16," Angle ~s"),[OrigA]),?DEGREE], 543 Str2 = [wings_util:format(?__(12,"\n Current Angle ~s"),[CurrA]),?DEGREE], 544 Str3 = [wings_util:format(?__(13,"\n Difference ~s"),[A2]),?DEGREE], 545 StrFinal = [Str1++Str2++Str3], 546 {none,?__(15," Original")++StrFinal}; 547 false -> 548 {none,?__(14,"Select exactly two edges or two faces to define angle")} 549 end; 550 551check_selection(sub_angle,none,none,_St,_OrigSt) -> 552 {none,?__(14,"Select exactly two edges or two faces to define angle")}; 553 554check_selection(scale,Axis1,Axis2,#st{selmode=edge}=St,OrigSt) -> 555 Original = add_edges(Axis1,OrigSt), 556 Current = add_edges(Axis2,St), 557 case Current < ?NONZERO of 558 true -> 559 {none,?__(3,"Current length is too short. Select edges that aren't perpendicular to the chosen axis.")}; 560 false -> 561 Percent = Original/Current, 562 case Percent < ?NONZERO of 563 true -> 564 {none,?__(5,"Resulting percentage is to small")}; 565 false -> 566 OStr = wings_util:nice_float(Original), 567 PStr = wings_util:nice_float(Percent*100), 568 RStr = wings_util:nice_float(1/Percent*100), 569 CStr = wings_util:nice_float(Current), 570 AxStr1 = axis_to_string(Axis1), 571 AxStr2 = axis_to_string(Axis2), 572 Str = ?__(4," Original ~s ~s\n Current ~s ~s\n Percent ~s% Reciprocal ~s%"), 573 {none,wings_util:format(Str, [AxStr1,OStr,AxStr2,CStr,PStr,RStr])} 574 end 575 end; 576 577check_selection(difference,Axis1,Axis2,#st{selmode=edge}=St,OrigSt) -> 578 Original = add_edges(Axis1,OrigSt), 579 Current = add_edges(Axis2,St), 580 Difference = abs(Original - Current), 581 case Difference < ?NONZERO of 582 true -> 583 {none,?__(6,"Difference is too small")}; 584 false -> 585 OStr = wings_util:nice_float(Original), 586 CStr = wings_util:nice_float(Current), 587 DStr = wings_util:nice_float(Difference), 588 AxStr1 = axis_to_string(Axis1), 589 AxStr2 = axis_to_string(Axis2), 590 Str = ?__(7," Original ~s ~s\n Current ~s ~s\n Difference ~s"), 591 {none,wings_util:format(Str,[AxStr1,OStr,AxStr2,CStr,DStr])} 592 end; 593 594check_selection(center,Axis1,_Axis2,St,OrigSt) -> 595 Original = wings_sel:center(OrigSt), 596 Current = wings_sel:center(St), 597 Distance = get_distance(Axis1,Original,Current), 598 case Distance < ?NONZERO of 599 true -> 600 {none,?__(8,"Distance between centers is too short")}; 601 false -> 602 OStr = axis_to_string({center,Original}), 603 CStr = axis_to_string({center,Current}), 604 DStr = wings_util:nice_float(Distance), 605 AxStr1 = axis_to_string(Axis1), 606 Str = ?__(9," Original center ~s\n Current center ~s\n Distance ~s ~s"), 607 {none,wings_util:format(Str,[OStr,CStr,DStr,AxStr1])} 608 end; 609 610check_selection(scale_area,Axis1,Axis2,St,OrigSt) -> 611 Original = add_areas(Axis1,OrigSt), 612 Current = add_areas(Axis2,St), 613 case Current < ?NONZERO of 614 true -> 615 {none,?__(10,"Current area is too small")}; 616 false -> 617 Percent = Original/Current, 618 case Percent < ?NONZERO of 619 true -> 620 {none,?__(5,"Resulting percentage is to small")}; 621 false -> 622 OStr = wings_util:nice_float(Original), 623 PStr = wings_util:nice_float(Percent*100), 624 RStr = wings_util:nice_float(1/Percent*100), 625 CStr = wings_util:nice_float(Current), 626 AxStr1 = axis_to_string(Axis1), 627 AxStr2 = axis_to_string(Axis2), 628 Str = ?__(4," Original ~s ~s\n Current ~s ~s\n Percent ~s% Reciprocal ~s%"), 629 {none,wings_util:format(Str, [AxStr1,OStr,AxStr2,CStr,PStr,RStr])} 630 end 631 end. 632 633axis_to_string(Axis) -> 634 case Axis of 635 normal -> 636 []; 637 {center,{_,_,_}} -> 638 {center,{X,Y,Z}} = Axis, 639 X1 = wings_util:nice_float(X), 640 Y1 = wings_util:nice_float(Y), 641 Z1 = wings_util:nice_float(Z), 642 Str = "<~s ~s ~s>", 643 wings_util:format(Str,[X1,Y1,Z1]); 644 {_,_,_} -> 645 {X,Y,Z} = Axis, 646 X1 = wings_util:nice_float(X), 647 Y1 = wings_util:nice_float(Y), 648 Z1 = wings_util:nice_float(Z), 649 Str = ?__(2,"along vector <~s ~s ~s>"), 650 wings_util:format(Str,[X1,Y1,Z1]); 651 {_,_} -> 652 {{X,Y,Z},_Point} = Axis, 653 X1 = wings_util:nice_float(X), 654 Y1 = wings_util:nice_float(Y), 655 Z1 = wings_util:nice_float(Z), 656 Str = ?__(2,"along vector <~s ~s ~s>"), 657 wings_util:format(Str,[X1,Y1,Z1]); 658 _xyz -> 659 Str = ?__(3,"along ~s axis"), 660 wings_util:format(Str,[wings_s:dir(Axis)]) 661 end. 662 663%%% Distance functions 664distance(Axis,St) -> 665 Set = atom_to_list(wings_pref:get_value(con_dist_set)), 666 Keys = mod_key_combo(), 667 Length = add_edges(Axis,St), 668 length_check(Axis,Length), 669 set_constraint(Keys, Set, Length), 670 St. 671 672average(Axis,#st{sel=Sel}=St) -> 673 Set = atom_to_list(wings_pref:get_value(con_dist_set)), 674 Keys = mod_key_combo(), 675 EdgeNum = lists:foldl(fun({_Id,Sel0}, A) -> 676 gb_sets:size(Sel0)+A 677 end, 0, Sel), 678 Length = add_edges(Axis,St), 679 AvgLength = Length/EdgeNum, 680 length_check(Axis,AvgLength), 681 set_constraint(Keys, Set, AvgLength), 682 St. 683 684length_check(Axis,Length) -> 685 case Length < ?NONZERO of 686 true -> 687 case Axis of 688 area -> 689 wings_u:error_msg(?__(1,"Selection must have an area greater than zero")); 690 normal -> 691 wings_u:error_msg(?__(2,"Selection must have length greater than zero")); 692 {_,_,_} -> 693 wings_u:error_msg(?__(3,"Length along vector is too short")); 694 _xyz -> 695 AxStr = wings_s:dir(Axis), 696 Str = ?__(4,"Length along ~s axis is too short"), 697 wings_u:error_msg(wings_util:format(Str,[AxStr])) 698 end; 699 false -> okay 700 end. 701 702add_edges(Axis,St) -> 703 wings_sel:fold(fun(Edges,We,Acc)-> 704 Es = gb_sets:to_list(Edges), 705 add_edges(Axis,Es,We) + Acc 706 end, 0, St). 707add_edges(Axis,Es,We) -> 708 lists:foldl(fun(Edge,A)-> 709 #we{es=Etab} = We, 710 #edge{vs=Va,ve=Vb} = array:get(Edge,Etab), 711 Pos1 = wings_vertex:pos(Va,We), 712 Pos2 = wings_vertex:pos(Vb,We), 713 get_distance(Axis,Pos1,Pos2) + A 714 end, 0, Es). 715 716get_distance(Axis, {Xa,Ya,Za}, {Xb,Yb,Zb}) -> 717 case Axis of 718 x -> 719 abs(e3d_vec:dist({Xa,0.0,0.0},{Xb,0.0,0.0})); 720 y -> 721 abs(e3d_vec:dist({0.0,Ya,0.0},{0.0,Yb,0.0})); 722 z -> 723 abs(e3d_vec:dist({0.0,0.0,Za},{0.0,0.0,Zb})); 724 normal -> 725 abs(e3d_vec:dist({Xa,Ya,Za},{Xb,Yb,Zb})); 726 {_,_,_} -> 727 {Vx,Vy,Vz} = e3d_vec:norm(Axis), 728 abs(Vx*(Xa-Xb)+Vy*(Ya-Yb)+Vz*(Za-Zb)) 729 end. 730%%% Angle selection checking 731check_element(#st{sel=[{_,Sel}]}) -> 732 case gb_sets:size(Sel) of 733 1 -> ok; 734 _ -> element_error() 735 end; 736check_element(_St) -> 737 element_error(). 738 739-spec element_error() -> no_return(). 740element_error() -> 741 Str = ?__(1,"Exactly one element must be selected"), 742 wings_u:error_msg(Str). 743 744check_angle_sel(#st{sel=[{_,Sel}]}) -> 745 case gb_sets:size(Sel) of 746 2 -> ok; 747 _ -> angle_error() 748 end; 749check_angle_sel(#st{sel=[{_,Sel1},{_,Sel2}]}) -> 750 case gb_sets:size(Sel1) == 1 andalso gb_sets:size(Sel2) == 1 of 751 true -> ok; 752 false -> angle_error() 753 end; 754check_angle_sel(_St) -> 755 angle_error(). 756 757-spec angle_error() -> no_return(). 758angle_error() -> 759 wings_u:error_msg(?__(1,"Exactly two elements must be selected")). 760 761%%% Main Angle functions 762sub_angle(Axis, St) -> 763 Keys = mod_key_combo(), 764 M = case Axis of 765 fifteen -> 15.0; 766 twenty_two -> 22.5; 767 thirty -> 30.0; 768 forty_five -> 45.0; 769 sixty -> 60.0; 770 ninety -> 90.0; 771 _ -> measure_angle(normal,Axis) 772 end, 773 N = measure_angle(normal,St), 774 Angle = abs(M - N), 775 set_angle(Keys,Angle,St). 776 777angle(Axis,St) -> 778 Keys = mod_key_combo(), 779 Angle = measure_angle(Axis,St), 780 set_angle(Keys,Angle,St). 781 782to_axis(Axis,#st{selmode=edge,shapes=Shs,sel=[{Id,Sel}]}=St) -> 783 Keys = mod_key_combo(), 784 Vec1 = wings_util:make_vector(Axis), 785 We = gb_trees:get(Id, Shs), 786 [Edge] = gb_sets:to_list(Sel), 787 #edge{vs=V0s,ve=V0e} = array:get(Edge, We#we.es), 788 Pos1 = wings_vertex:pos(V0s, We), 789 Pos2 = wings_vertex:pos(V0e, We), 790 Vec2 = e3d_vec:sub(Pos1,Pos2), 791 Norm1 = e3d_vec:norm(Vec1), 792 Norm2 = e3d_vec:norm(Vec2), 793 Angle = e3d_vec:degrees(Norm1,Norm2), 794 set_angle(Keys,Angle,St); 795 796to_axis(Axis,#st{selmode=face,shapes=Shs,sel=[{Id,Sel}]}=St) -> 797 Keys = mod_key_combo(), 798 Vec1 = wings_util:make_vector(Axis), 799 We = gb_trees:get(Id, Shs), 800 [Face] = gb_sets:to_list(Sel), 801 Norm1 = wings_face:normal(Face,We), 802 Norm2 = e3d_vec:norm(Vec1), 803 Angle = e3d_vec:degrees(Norm1,Norm2), 804 set_angle(Keys,Angle,St). 805 806measure_angle(Axis,#st{selmode=edge,shapes=Shs,sel=[{Id0,Sel0},{Id1,Sel1}]}) -> 807 We0 = gb_trees:get(Id0, Shs), 808 We1 = gb_trees:get(Id1, Shs), 809 [E0] = gb_sets:to_list(Sel0), 810 [E1] = gb_sets:to_list(Sel1), 811 #edge{vs=V0s,ve=V0e} = array:get(E0, We0#we.es), 812 #edge{vs=V1s,ve=V1e} = array:get(E1, We1#we.es), 813 Pos1 = wings_vertex:pos(V0s, We0), 814 Pos2 = wings_vertex:pos(V0e, We0), 815 Pos3 = wings_vertex:pos(V1s, We1), 816 Pos4 = wings_vertex:pos(V1e, We1), 817 [Vec0,Vec1] = get_angle(Axis,[Pos1,Pos2,Pos3,Pos4]), 818 raw_angle_to_angle(Vec0,Vec1,V0s,V0e,V1s,V1e); 819 820measure_angle(Axis,#st{selmode=edge,shapes=Shs,sel=[{Id,Sel}]}) -> 821 We = gb_trees:get(Id, Shs), 822 [E0,E1] = gb_sets:to_list(Sel), 823 #edge{vs=V0s,ve=V0e} = array:get(E0, We#we.es), 824 #edge{vs=V1s,ve=V1e} = array:get(E1, We#we.es), 825 Pos1 = wings_vertex:pos(V0s, We), 826 Pos2 = wings_vertex:pos(V0e, We), 827 Pos3 = wings_vertex:pos(V1s, We), 828 Pos4 = wings_vertex:pos(V1e, We), 829 [Vec0,Vec1] = get_angle(Axis,[Pos1,Pos2,Pos3,Pos4]), 830 raw_angle_to_angle(Vec0,Vec1,V0s,V0e,V1s,V1e); 831 832measure_angle(Axis,#st{selmode=face,shapes=Shs,sel=[{Id0,Sel0},{Id1,Sel1}]}) -> 833 We0 = gb_trees:get(Id0, Shs), 834 We1 = gb_trees:get(Id1, Shs), 835 [F0] = gb_sets:to_list(Sel0), 836 [F1] = gb_sets:to_list(Sel1), 837 N0 = wings_face:normal(F0,We0), 838 N1 = wings_face:normal(F1,We1), 839 Pos1 = wings_face:center(F0,We0), 840 Pos2 = e3d_vec:add(Pos1,e3d_vec:mul(N0,0.2)), 841 Pos3 = wings_face:center(F1,We1), 842 Pos4 = e3d_vec:add(Pos3,e3d_vec:mul(N1,0.2)), 843 [Vec0,Vec1] = get_angle(Axis,[Pos1,Pos2,Pos3,Pos4]), 844 e3d_vec:degrees(Vec0,Vec1); 845 846measure_angle(Axis,#st{selmode=face,shapes=Shs,sel=[{Id,Sel}]}) -> 847 We = gb_trees:get(Id, Shs), 848 [F0,F1] = gb_sets:to_list(Sel), 849 N0 = wings_face:normal(F0,We), 850 N1 = wings_face:normal(F1,We), 851 Pos1 = wings_face:center(F0,We), 852 Pos2 = e3d_vec:add(Pos1,e3d_vec:mul(N0,0.2)), 853 Pos3 = wings_face:center(F1,We), 854 Pos4 = e3d_vec:add(Pos3,e3d_vec:mul(N1,0.2)), 855 [Vec0,Vec1] = get_angle(Axis,[Pos1,Pos2,Pos3,Pos4]), 856 e3d_vec:degrees(Vec0,Vec1). 857 858get_angle(Axis,Vlist) -> 859 [{X0s,Y0s,Z0s},{X0e,Y0e,Z0e},{X1s,Y1s,Z1s},{X1e,Y1e,Z1e}] = Vlist, 860 case Axis of 861 x -> 862 [{0.0, Y0e-Y0s, Z0e-Z0s}, 863 {0.0, Y1e-Y1s, Z1e-Z1s}]; 864 y -> 865 [{X0e-X0s, 0.0, Z0e-Z0s}, 866 {X1e-X1s, 0.0, Z1e-Z1s}]; 867 z -> 868 [{X0e-X0s, Y0e-Y0s, 0.0}, 869 {X1e-X1s, Y1e-Y1s, 0.0}]; 870 normal -> 871 [{X0e-X0s, Y0e-Y0s, Z0e-Z0s}, 872 {X1e-X1s, Y1e-Y1s, Z1e-Z1s}]; 873 {_,_} -> %% from axis_point ask 874 {PlaneNorm,PlanePoint} = Axis, 875 Pn = e3d_vec:norm(PlaneNorm), 876 Dp = e3d_vec:dot(Pn, Pn), 877 VPoints = lists:foldl(fun(Point,A) -> 878 M0 = e3d_vec:dot(e3d_vec:sub(PlanePoint, Point),Pn)/Dp, 879 M1 = e3d_vec:add(Point, e3d_vec:mul(Pn, M0)), 880 [M1|A] 881 end,[],Vlist), 882 [{X2s,Y2s,Z2s},{X2e,Y2e,Z2e},{X3s,Y3s,Z3s},{X3e,Y3e,Z3e}] = VPoints, 883 884 [{X2e-X2s, Y2e-Y2s, Z2e-Z2s},{X3e-X3s, Y3e-Y3s, Z3e-Z3s}] 885 end. 886 887raw_angle_to_angle(Vec0,Vec1,V0s,V0e,V1s,V1e) -> 888 RawAngle = e3d_vec:degrees(Vec0, Vec1), 889 case {V0s,V0e} of 890 {V1s,_} -> RawAngle; 891 {_,V1e} -> RawAngle; 892 {V1e,_} -> 180.0 - RawAngle; 893 {_,V1s} -> 180.0 - RawAngle; 894 {_,_} -> RawAngle 895 end. 896 897set_angle(Keys, Angle, St) -> 898 A = case Angle >= ?NONZERO of 899 true -> Angle; 900 false-> 180.0 901 end, 902 set_constraint(Keys, "con_rot_", A), 903 St. 904 905%%% Scale functions 906scale({Axis1,Axis2},#st{selmode=edge}=St) -> 907 Keys = mod_key_combo(), 908 Length = add_edges(Axis1,St), 909 length_check(Axis1,Length), 910 wings:ask(secondary_sel_ask(scale,Axis1,Axis2,St), St, fun (St0,OrigSt) -> 911 scale(Axis1,Axis2,Keys,OrigSt,St0) 912 end); 913 914scale({Axis1,Axis2},#st{selmode=face}=St) -> 915 Keys = mod_key_combo(), 916 Area = add_areas(Axis1,St), 917 length_check(area,Area), 918 wings:ask(secondary_sel_ask(scale_area,Axis1,Axis2,St), St, fun (St0,OrigSt) -> 919 scale(Axis1,Axis2,Keys,OrigSt,St0) 920 end). 921 922scale(Axis1,Axis2,Keys,OrigSt,#st{selmode=edge}=St) -> 923 Length1 = add_edges(Axis1,OrigSt), 924 Length2 = add_edges(Axis2,St), 925 Percent = Length1/Length2, 926 set_constraint(Keys, "con_scale_", Percent), 927 OrigSt; 928 929scale(Axis1,Axis2,Keys,OrigSt,#st{selmode=face}=St) -> 930 Area1 = add_areas(Axis1,OrigSt), 931 Area2 = add_areas(Axis2,St), 932 Percent = Area1/Area2, 933 set_constraint(Keys, "con_scale_", Percent), 934 OrigSt. 935 936add_areas(Axis,St) -> 937 wings_sel:fold(fun(Faces,We,Acc) -> 938 Fs = gb_sets:to_list(Faces), 939 add_areas(Axis,Fs,We) + Acc 940 end, 0, St). 941 942add_areas(Axis,Fs,We) -> 943 lists:foldl(fun(Face,A) -> 944 Area = case Axis of 945 normal -> wings_face:area(Face,We); 946 _axis -> get_area(Axis,Face,We) 947 end, 948 Area + A 949 end, 0, Fs). 950 951get_area(Axis,Face,We) -> 952 #we{vp=Vtab} = We, 953 Vs = wings_face:vertices_ccw(Face, We), 954 Vlist0 = [array:get(V, Vtab) || V <- Vs], 955 Vlist1 = flatten_vpos_to_axis(Axis,Vlist0), 956 FaceVs = lists:seq(0, length(Vs)-1), 957 E3dFaces = [#e3d_face{vs=FaceVs}], 958 [Area] = e3d_mesh:face_areas(E3dFaces, Vlist1), 959 Area. 960 961flatten_vpos_to_axis(Axis,Vlist) -> 962 {PlaneNorm,PlanePoint} = case Axis of 963 x -> {{1.0,0.0,0.0},{1.0,0.0,0.0}}; 964 y -> {{0.0,1.0,0.0},{0.0,1.0,0.0}}; 965 z -> {{0.0,0.0,1.0},{0.0,0.0,1.0}}; 966 {_,_} -> Axis 967 end, 968 Pn = e3d_vec:norm(PlaneNorm), 969 Dp = e3d_vec:dot(Pn, Pn), 970 NewVlist = lists:foldl(fun(Point,A) -> 971 M0 = e3d_vec:dot(e3d_vec:sub(PlanePoint, Point),Pn)/Dp, 972 M1 = e3d_vec:add(Point, e3d_vec:mul(Pn, M0)), 973 [M1|A] 974 end,[],Vlist), 975 NewVlist. 976 977%%% Difference functions 978difference({Axis1,Axis2},St) -> 979 Keys = mod_key_combo(), 980 wings:ask(secondary_sel_ask(difference,Axis1,Axis2,St), St, fun (St0,OrigSt) -> 981 difference(Axis1,Axis2,Keys,OrigSt,St0) 982 end). 983 984difference(Axis1,Axis2,Keys1,OrigSt,St) -> 985 Keys2 = mod_key_combo(), 986 Keys = case Keys2 of 987 {false,false,false} -> Keys1; 988 _if_keys_held_again -> Keys2 989 end, 990 Set = atom_to_list(wings_pref:get_value(con_dist_set)), 991 Length1 = add_edges(Axis1,OrigSt), 992 Length2 = add_edges(Axis2,St), 993 Difference = abs(Length1-Length2), 994 set_constraint(Keys, Set, Difference), 995 OrigSt. 996 997%%% Centers functions 998centers(Axis,St) -> 999 Keys = mod_key_combo(), 1000 wings:ask(secondary_sel_ask(center,Axis,none,St), St, fun (St0,OrigSt) -> 1001 centers(Axis,Keys,OrigSt,St0) 1002 end). 1003 1004centers(Axis,Keys1,OrigSt,St) -> 1005 Keys2 = mod_key_combo(), 1006 Keys = case Keys2 of 1007 {false,false,false} -> Keys1; 1008 _if_keys_held_again -> Keys2 1009 end, 1010 Set = atom_to_list(wings_pref:get_value(con_dist_set)), 1011 Center1 = wings_sel:center(OrigSt), 1012 Center2 = wings_sel:center(St), 1013 Distance = get_distance(Axis,Center1,Center2), 1014 set_constraint(Keys, Set, Distance), 1015 OrigSt. 1016 1017%%% Modifier keys 1018mod_key_combo() -> 1019 Shift = wings_io:is_modkey_pressed(?KMOD_SHIFT), 1020 Ctrl = wings_io:is_modkey_pressed(?KMOD_CTRL), 1021 Alt = wings_io:is_modkey_pressed(?KMOD_ALT), 1022 {Shift,Ctrl,Alt}. 1023 1024%%% Set preferences 1025set_constraint({Shift,Ctrl,Alt}, Key, Val) -> 1026 ModKeyCombo = case {Shift,Ctrl,Alt} of 1027 {true,false,false} -> "shift"; 1028 {false,true,false} -> "ctrl"; 1029 {true,true,false} -> "ctrl_shift"; 1030 {false,false,true} -> "alt"; 1031 {true,false,true} -> "shift_alt"; 1032 {false,true,true} -> "ctrl_alt"; 1033 {true,true,true} -> "ctrl_shift_alt"; 1034 {false,false,false} -> 1035 case Key of 1036 "con_dist_" -> atom_to_list(wings_pref:get_value(con_dist_default)); 1037 "con_dist_a_" -> atom_to_list(wings_pref:get_value(con_dist_default)); 1038 "con_rot_" -> atom_to_list(wings_pref:get_value(con_rot_default)); 1039 "con_scale_" -> atom_to_list(wings_pref:get_value(con_scale_default)) 1040 end 1041 end, 1042 ComboStr = mod(ModKeyCombo), 1043 Tag = tag(Key), 1044 Msg = io_lib:format(?__(1,"The ~ts constraint bound to ~ts is now set to ~p"),[Tag,ComboStr,Val]), 1045 io:format("~ts\n",[Msg]), 1046 wings_u:message(Msg), 1047 wings_pref:set_value(list_to_atom(Key++ModKeyCombo), Val). 1048 1049tag(Key) -> 1050 case Key of 1051 "con_rot_" -> ?__(1,"Rotation"); 1052 "con_scale_" -> ?__(2,"Scale Factor"); 1053 "con_dist_" -> ?__(3,"Distance"); 1054 "con_dist_a_" -> ?__(4,"Alternate Distance") 1055 end. 1056 1057mod(ModKeyCombo) -> 1058 wings_util:stringify(list_to_atom(ModKeyCombo)). 1059