1%% 2%% wpc_autouv.erl -- 3%% 4%% A semi-simple semi-automatic UV-mapping semi-plugin. 5%% 6%% Copyright (c) 2002-2011 Dan Gudmundsson, 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(wpc_autouv). 15 16-define(NEED_OPENGL, 1). 17-define(NEED_ESDL, 1). 18 19-include_lib("src/wings.hrl"). 20-include_lib("e3d/e3d_image.hrl"). 21-include("auv.hrl"). 22 23-export([init/0,menu/2,command/2,redraw/1]). 24-export([handle_event/2,bg_image/0]). %% Debug 25-import(lists, [sort/1,keysort/2,map/2,foldl/3,reverse/1,keysearch/3]). 26 27%% Exports to auv_seg_ui. 28-export([init_show_maps/4]). 29 30init() -> true. 31 32menu({body}, Menu) -> 33 case wpc_snap_win:active() of 34 true -> 35 Menu; 36 false -> 37 Menu ++ [separator,auv_menu()] 38 end; 39menu({face}, Menu) -> 40 case wpc_snap_win:active() of 41 true -> 42 Menu; 43 false -> 44 Menu ++ [separator,auv_menu()] 45 end; 46menu({window}, Menu) -> 47 Menu ++ [separator, 48 {?__(1,"UV Editor Window"),uv_editor_window, 49 ?__(2,"Open a UV Editor window for each selected object")}]; 50menu(_Dbg, Menu) -> 51 Menu. 52 53auv_menu() -> 54 {?__(1,"UV Mapping"), {?MODULE, fun auv_menu/2}}. 55auv_menu(help,_) -> 56 {?__(2,"Generate UV mapping or texture"), 57 ?__(25,"Re-segment object(s)"), 58 ?__(3,"Force to segmenting mode (delete old segments)")}; 59auv_menu(1,_What) -> {?MODULE, segment}; 60auv_menu(2,_) -> {?MODULE, segment_old}; 61auv_menu(3,_) -> {?MODULE, force_seg}. 62 63auv_show_menu(label) -> 64 ?__(1,"Show/Hide Background Image"); 65auv_show_menu(help) -> 66 ?__(2,"Toggle display of the background texture image"); 67auv_show_menu(Action) -> 68 Cmd = {show,toggle_background}, 69 case Action of 70 true -> 71 Label = auv_show_menu(label), 72 Help = auv_show_menu(help), 73 wings_menu:update_menu(view, Cmd, {append, 0, Label},Help); 74 false -> 75 wings_menu:update_menu(view, Cmd, delete) 76 end. 77 78auv_export_menu(label) -> 79 ?__(1,"Export UV..."); 80auv_export_menu(help) -> 81 ?__(2,"Exports the UV as cartoon edges (.eps, .svg)"). 82 83command({body,{?MODULE, Op}} , St) -> 84 start_uvmap(Op, St); 85command({face,{?MODULE, Op}} , St) -> 86 start_uvmap(Op, St); 87command({?MODULE, Op}, St) -> 88 start_uvmap(Op, St); 89command({window,uv_editor_window}, St) -> 90 start_uvmap(edit, St); 91command(_Cmd, _) -> 92 next. 93 94start_uvmap(edit, #st{sel=[]}) -> wings_u:error_msg(?__(1,"Nothing selected")); 95start_uvmap(Action, #st{sel=Sel}=St) -> 96 start_uvmap_1(Sel, Action, St). 97 98start_uvmap_1([{Id,_}|T], Action, St) -> 99 EditWin = {autouv,Id}, 100 EditExists = wings_wm:is_window(EditWin), 101 SegWin = {autouv,{segment,Id}}, 102 SegExists = wings_wm:is_window(SegWin), 103 case segment_or_edit(Action,Id,St) of 104 {edit,Fs} when EditExists -> 105 wings_wm:send(EditWin, {add_faces,Fs,St}), 106 wings_wm:raise(EditWin); 107 {seg_ui,Fs,_} when SegExists -> 108 wings_wm:send(SegWin, {add_faces,Fs,St}), 109 wings_wm:raise(SegWin); 110 Op when element(1,Op) == edit -> 111 create_window(Op, EditWin, Id, St); 112 Op -> 113 create_window(Op, SegWin, Id, St) 114 end, 115 start_uvmap_1(T, Action, St); 116start_uvmap_1([], _, _) -> keep. 117 118segment_or_edit(edit, _Id, _St) -> {edit,object}; 119segment_or_edit(segment,Id,#st{selmode=face,sel=Sel,shapes=Shs}) -> 120 We = gb_trees:get(Id, Shs), 121 UVFs = gb_sets:from_ordset(wings_we:uv_mapped_faces(We)), 122 {value,{_,Fs}} = lists:keysearch(Id, 1, Sel), 123 case gb_sets:is_subset(Fs,UVFs) of 124 false -> {seg_ui,Fs,delete_old}; 125 true -> {edit, Fs} 126 end; 127segment_or_edit(segment,Id,#st{shapes=Shs}) -> 128 We = gb_trees:get(Id, Shs), 129 case wings_we:uv_mapped_faces(We) of 130 [] -> {seg_ui,object,delete_old}; 131 _ -> {edit,object} 132 end; 133segment_or_edit(force_seg,Id,#st{selmode=face,sel=Sel}) -> 134 {value,{_,Fs}} = lists:keysearch(Id, 1, Sel), 135 {seg_ui,Fs,delete_old}; 136segment_or_edit(force_seg,_Id,_) -> 137 {seg_ui,object,delete_old}; 138segment_or_edit(segment_old,Id,#st{selmode=face,sel=Sel}) -> 139 {value,{_,Fs}} = lists:keysearch(Id, 1, Sel), 140 {seg_ui,Fs,keep_old}; 141segment_or_edit(segment_old,_Id,_) -> 142 {seg_ui,object,keep_old}. 143 144create_window(Action, Name, Id, #st{shapes=Shs}=St) -> 145 #we{name=ObjName} = We = gb_trees:get(Id, Shs), 146 Op = {replace,fun(Ev) -> auv_event(Ev, St) end}, 147 Segment = if element(1,Action) == edit -> ""; true -> ?__(1,"Segmenting") end, 148 Title = "AutoUV "++ Segment ++": " ++ ObjName, 149 150 {Pos,Size} = init_drawarea(), 151 {Frame,Ps} = wings_frame:make_win(Title, [{size, Size}, {pos, Pos}]), 152 Canvas = wings_gl:window(Frame, ?GET(gl_context), true, true), 153 Props = [{display_data,Name}|wings_view:initial_properties()++Ps], 154 wings_wm:toplevel(Name, Canvas, Props, Op), 155 wings_wm:send(Name, {init,{Action,We}}), 156 Frame. 157 158auv_event({init,Op}, St) -> 159 wings:init_opengl(St), 160 case Op of 161 {{edit,What},We} -> start_edit(What, We, St); 162 {{seg_ui,_,Oper},We} -> auv_seg_ui:start(Oper, We, We, St) 163 end; 164auv_event(redraw, _) -> 165 wings_wm:clear_background(), 166 keep; 167auv_event({crash,Crash}, _) -> 168 wings_u:win_crash(Crash), 169 delete; 170auv_event(_Ev, _) -> keep. 171 172%%% 173%%% Start the UV editor. 174%%% 175 176start_edit(Mode, We, St) -> 177 MatNames0 = wings_facemat:all(We), 178 MatNames1 = sofs:from_external(MatNames0, [{face,material}]), 179 MatNames2 = sofs:converse(MatNames1), 180 MatNames3 = sofs:relation_to_family(MatNames2), 181 MatNames4 = sofs:to_external(MatNames3), 182 MatNames = [Mat || {Name,_}=Mat <- MatNames4, get_texture(Name, St) /= false], 183 case MatNames of 184 [{MatName,_}] -> 185 do_edit(MatName, Mode, We, St); 186 _ -> 187 do_edit(none, Mode, We, St) 188 end. 189 190do_edit(MatName, Mode, We, GeomSt) -> 191 AuvSt = create_uv_state(gb_trees:empty(), MatName, Mode, We, GeomSt), 192 new_geom_state(GeomSt, AuvSt). 193 194init_show_maps(Charts0, Fs, #we{name=WeName,id=Id}, GeomSt0) -> 195 Charts1 = auv_placement:place_areas(Charts0), 196 Charts = gb_trees:from_orddict(keysort(1, Charts1)), 197 MatName0 = list_to_atom(WeName++"_auv"), 198 {GeomSt1,MatName} = 199 case gb_trees:is_defined(MatName0, GeomSt0#st.mat) of 200 true -> 201 {GeomSt0,MatName0}; 202 false -> 203 Tx = bg_img_id(), 204 add_material(Tx, WeName, GeomSt0) 205 end, 206 GeomSt = insert_initial_uvcoords(Charts, Id, MatName, GeomSt1), 207 EditWin = {autouv,Id}, 208 case wings_wm:is_window(EditWin) of 209 true -> 210 wings_wm:send(EditWin, {add_faces,Fs,GeomSt}), 211 wings_wm:send(geom, {new_state,GeomSt}); 212 false -> 213 %% we are going to ensure to open the AutoUV window in the same 214 %% display as the segment window was, since it can be in another 215 %% than the one in which the main window is. 216 SegWin = wings_wm:this_win(), 217 {X0,Y0} = wxWindow:getPosition(SegWin), 218 Pos = wxWindow:clientToScreen(SegWin,X0,Y0), 219 Win = create_window({edit,Fs}, EditWin, Id, GeomSt), 220 wxWindow:move(Win,Pos), 221 wings_wm:send(geom, {new_state,GeomSt}) 222 end, 223 GeomSt. 224 225create_uv_state(Charts, MatName, Fs, We, #st{shapes=Shs0}=GeomSt) -> 226 wings:mode_restriction([vertex,edge,face,body]), 227 wings_wm:current_state(#st{selmode=body,sel=[]}), 228 229 Shs = gb_trees:update(We#we.id, We#we{fs=undefined,es=array:new()}, Shs0), 230 FakeGeomSt = GeomSt#st{sel=[],shapes=Shs}, 231 232 Image = case get_texture(MatName,GeomSt) of 233 false -> bg_img_id(); 234 ImId -> ImId 235 end, 236 Uvs = #uvstate{st=wpa:sel_set(face, [], FakeGeomSt), 237 id = We#we.id, 238 mode = Fs, 239 bg_img = Image, 240 matname = MatName}, 241 St = FakeGeomSt#st{selmode=body,sel=[],shapes=Charts,bb=Uvs, 242 repeatable=ignore,ask_args=none,drag_args=none}, 243 Name = wings_wm:this(), 244 245 View = #view{origin={0.0,0.0,0.0}, 246 distance=0.65, 247 azimuth=0.0, 248 elevation=0.0, 249 pan_x=-0.5, 250 pan_y=-0.5, 251 fov=90.0, 252 hither=0.0001, 253 yon=50.0}, 254 wings_view:set_current(View), 255 256 wings_wm:set_prop(Name, drag_filter, fun drag_filter/1), 257 wings_wm:set_prop(show_wire_backfaces, true), 258 wings_wm:set_prop(show_info_text, false), %% Users want this 259 wings_wm:set_prop(orthogonal_view, true), 260 wings_wm:set_prop(show_axes, false), 261 wings_wm:set_prop(show_groundplane, false), 262 wings_wm:set_prop(wireframed_objects, 263 gb_sets:from_list(gb_trees:keys(Charts))), 264 wings_wm:set_prop(allow_rotation, false), 265 wings_wm:set_prop(select_backface, true), 266 267 wings_wm:later(got_focus), 268 269 Win = wings_wm:this(), 270 case ?GET({?MODULE,show_background}) of 271 undefined -> 272 ?SET({?MODULE,show_background}, true); 273 _ -> ignore 274 end, 275 wings:register_postdraw_hook(Win, ?MODULE, 276 fun draw_background/1), 277 St. 278 279insert_initial_uvcoords(Charts, Id, MatName, #st{shapes=Shs0}=St) -> 280 We0 = gb_trees:get(Id, Shs0), 281 We1 = update_uvs(gb_trees:values(Charts), We0), 282 We2 = preserve_old_materials(We1, St), 283 We = insert_material(Charts, MatName, We2), 284 Shs = gb_trees:update(Id, We, Shs0), 285 St#st{shapes=Shs}. 286 287update_selected_uvcoords(#st{bb=Uvs}=St) -> 288 Charts = wpa:sel_fold(fun(_, We, Acc) -> [We|Acc] end, [], St), 289 #uvstate{st=#st{shapes=Shs0}=GeomSt0,id=Id} = Uvs, 290 We0 = gb_trees:get(Id, Shs0), 291 We = update_uvs(Charts, We0), 292 Shs = gb_trees:update(Id, We, Shs0), 293 GeomSt = GeomSt0#st{shapes=Shs}, 294 wings_wm:send(geom, {new_state,GeomSt}), 295 clear_temp_sel(St#st{bb=Uvs#uvstate{st=GeomSt}}). 296 297%% update_uvs(Charts, We0) -> We 298%% Update the UV coordinates for the original model. 299update_uvs([#we{vp=Vpos0,name=#ch{vmap=Vmap}}=ChartWe|Cs], We0) -> 300 VFace0 = wings_face:fold_faces( 301 fun(Face, V, _, _, A) -> 302 [{V,Face}|A] 303 end, [], 304 wings_we:visible(ChartWe), ChartWe), 305 VFace1 = sofs:relation(VFace0), 306 VFace2 = sofs:relation_to_family(VFace1), 307 VFace = sofs:to_external(VFace2), 308 Vpos = array:sparse_to_orddict(Vpos0), 309 We = update_uvs_1(Vpos, VFace, Vmap, We0), 310 update_uvs(Cs, We); 311update_uvs([], We) -> We. 312 313update_uvs_1([{V0,{X,Y,_}}|Vs], [{V0,Fs}|VFs], Vmap, We0) -> 314 UV = {X,Y}, 315 V = auv_segment:map_vertex(V0, Vmap), 316 We = wings_va:set_vtx_face_uvs(V, Fs, UV, We0), 317 update_uvs_1(Vs, VFs, Vmap, We); 318update_uvs_1([{V0,none}|Vs], [{V0,Fs}|VFs], Vmap, We0) -> 319 V = auv_segment:map_vertex(V0, Vmap), 320 We = wings_va:set_vtx_face_uvs(V, Fs, none, We0), 321 update_uvs_1(Vs, VFs, Vmap, We); 322update_uvs_1([], [], _, We) -> We. 323 324%% preserve_old_materials(We0) -> We 325%% If the object contains materials with colors and no 326%% vertex colors, convert the materials to vertex colors. 327preserve_old_materials(We, St) -> 328 case not wings_va:any_colors(We) andalso 329 wings_facemat:any_interesting_materials(We) of 330 true -> 331 wings_we:uv_to_color(We, St); 332 false -> 333 We 334 end. 335 336insert_material(Cs, MatName, We) -> 337 Faces = lists:append([wings_we:visible(W) || W <- gb_trees:values(Cs)]), 338 wings_facemat:assign(MatName, Faces, We). 339 340 341%%%%% Material handling 342 343get_texture(MatName, #st{mat=Materials}) -> 344 get_texture(MatName, Materials); 345get_texture(MatName, Materials) -> 346 case gb_trees:lookup(MatName, Materials) of 347 none -> false; 348 {value,Mat} -> 349 Maps = proplists:get_value(maps, Mat, []), 350 proplists:get_value(diffuse, Maps, false) 351 end. 352 353add_material(Tx, Name, St0) -> 354 MatName0 = list_to_atom(Name++"_auv"), 355 Mat = {MatName0,[{opengl,[]},{maps,[{diffuse,Tx}]}]}, 356 case wings_material:add_materials([Mat], St0) of 357 {St,[]} -> 358 {St,MatName0}; 359 {St,[{MatName0,MatName}]} -> 360 {St,MatName} 361 end. 362update_texture(Im = #e3d_image{},MatName,St) -> 363 catch wings_material:update_image(MatName, diffuse, Im, St), 364 {St,MatName}. 365 366bg_img_id() -> 367 Is = wings_image:images(), 368 case [ImId || {ImId,#e3d_image{name="auvBG"}} <- Is] of 369 [ImId] -> ImId; 370 _ -> wings_image:new("auvBG",bg_image()) 371 end. 372 373%%%% Menus. 374 375command_menu(body, X, Y) -> 376 First = [{?__(37,"Stretch optimization"), stretch_opt, 377 ?__(38,"Optimize the chart stretch")}, 378 separator], 379 Unfold = case erlang:system_info(wordsize) of 380 4 -> 381 [{?__(39,"Unfold"), lsqcm, ?__(40,"Unfold the chart")}]; 382 8 -> 383 [{?__(39,"Unfold"), lsqcm, ?__(40,"Unfold the chart")}, 384 {?__(391,"Unfold (slow)"),slim, 385 ?__(392,"Unfold the chart, slow for charts with many faces but better")}] 386 end, 387 Rest = [{?__(41,"Project Normal"), project, 388 ?__(42,"Project UVs from chart normal")}, 389 {?__(43,"Spherical"), sphere, 390 ?__(44,"Spherical mapping")} 391 ], 392 Remap = First ++ Unfold ++ Rest, 393 394 Menu = [{?__(2,"Move"), {move, move_directions(false)}, 395 ?__(3,"Move selected charts")}, 396 {?__(4,"Scale"), {scale, scale_directions(false) ++ 397 [separator] ++ stretch_directions() ++ 398 [separator, 399 {?__(411,"Normalize Sizes"), normalize, 400 ?__(412,"Normalize Chart Sizes so that each" 401 "chart get it's corresponding 2d area")}]}, 402 ?__(5,"Scale selected charts")}, 403 {?__(6,"Rotate"), rotate, ?__(7,"Rotate selected charts")}, 404 separator, 405 {?__(8,"Move to"), 406 {move_to, 407 [{?__(9,"Center"), center, ?__(10,"Move to Center")}, 408 {?__(11,"Center X"), center_x, ?__(12,"Move to horizontal center")}, 409 {?__(13,"Center Y"), center_y, ?__(14,"Move to vertical center")}, 410 {?__(15,"Bottom"), bottom, ?__(16,"Move to bottom border")}, 411 {?__(17,"Top"), top, ?__(18,"Move to top border")}, 412 {?__(19,"Left"), left, ?__(20,"Move to left border")}, 413 {?__(21,"Right"), right, ?__(22,"Move to right border")} 414 ]}, ?__(23,"Move charts to position")}, 415 {?__(93,"Align"), 416 {align, 417 [{?__(15,"Bottom"), bottom, ?__(95,"Align to bottom")}, 418 {?__(17,"Top"), top, ?__(96,"Align to top")}, 419 {?__(19,"Left"), left, ?__(97,"Align to left")}, 420 {?__(21,"Right"), right, ?__(98,"Align to right")} 421 ]}, ?__(94,"Align charts relative each other")}, 422 {?__(24,"Flip"),{flip, 423 [{?__(25,"Horizontal"),horizontal,?__(26,"Flip selection horizontally")}, 424 {?__(27,"Vertical"),vertical,?__(28,"Flip selection vertically")}]}, 425 ?__(29,"Flip selected charts")}, 426 separator, 427 {?__(30,"Tighten"),tighten, 428 ?__(31,"Move UV coordinates towards average midpoint")}, 429 separator, 430 {?__(32,"Hide"),hide,?__(33,"Hide selected charts but keep UV-coordinates")}, 431 {?__(34,"Delete"),delete,?__(35,"Remove UV-coordinates for the selected charts")}, 432 separator, 433 {?__(36,"ReMap UV"), {remap, Remap}, 434 ?__(45,"Calculate new UVs with chosen algorithm")} 435 ] ++ option_menu(), 436 wings_menu:popup_menu(X,Y, {auv,body}, Menu); 437command_menu(face, X, Y) -> 438 Scale = scale_directions(true), 439 Move = move_directions(true), 440 Menu = [{?__(47,"Move"),{move,Move},?__(48,"Move selected faces"),[magnet]}, 441 {?__(49,"Scale"),{scale,Scale},?__(50,"Scale selected faces"), [magnet]}, 442 {?__(51,"Rotate"),rotate,?__(52,"Rotate selected faces"), [magnet]}, 443 separator, 444 {?__(521,"Project-Unfold"), {remap, proj_lsqcm}, 445 ?__(522,"Project selected faces from normal and unfold the rest of chart")} 446 ] ++ option_menu(), 447 wings_menu:popup_menu(X,Y, {auv,face}, Menu); 448command_menu(edge, X, Y) -> 449 Scale = scale_directions(true), 450 Move = move_directions(true), 451 Align = 452 [{?__(53,"Free"),free,?__(54,"Rotate selection freely"), [magnet]}, 453 {?__(55,"Chart to X"), align_x, ?__(56,"Rotate chart to align selected edge to X-axis")}, 454 {?__(57,"Chart to Y"), align_y, ?__(58,"Rotate chart to align selected edge to Y-axis")}], 455 Menu = [{?__(60,"Move"),{move,Move},?__(61,"Move selected edges"),[magnet]}, 456 {?__(62,"Scale"),{scale,Scale},?__(63,"Scale selected edges"), [magnet]}, 457 {?__(64,"Rotate"),{rotate,Align},?__(65,"Rotate commands")}, 458 {?__(641,"Slide"),slide,?__(642,"Slide along neighbor edges")}, 459 {?__(643,"Distribute"), 460 {equal, 461 [{?__(25,"Horizontal"),horizontal,?__(644,"Distribute horizontally")}, 462 {?__(27,"Vertical"),vertical,?__(645,"Distribute vertically")}]}, 463 ?__(646,"Distribute vertices evenly")}, 464 separator, 465 {?__(66,"Stitch"), stitch, ?__(67,"Stitch edges/charts")}, 466 {?__(68,"Cut"), cut_edges, ?__(69,"Cut selected edges")} 467 ] ++ option_menu(), 468 wings_menu:popup_menu(X,Y, {auv,edge}, Menu); 469command_menu(vertex, X, Y) -> 470 Scale = scale_directions(true), 471 Move = move_directions(true), 472 Align = 473 [{?__(70,"Free"),free,?__(71,"Rotate selection freely"), [magnet]}, 474 {?__(72,"Chart to X"), align_x, 475 ?__(73,"Rotate chart to align (imaginary) edge joining selected verts to X-axis")}, 476 {?__(74,"Chart to Y"), align_y, 477 ?__(75,"Rotate chart to align (imaginary) edge joining selected verts to Y-axis")}], 478 479 Menu = [{?__(77,"Move"),{move,Move},?__(78,"Move selected vertices"),[magnet]}, 480 {?__(79,"Scale"),{scale,Scale},?__(80,"Scale selected vertices"), [magnet]}, 481 {?__(81,"Rotate"),{rotate,Align},?__(82,"Rotation commands")}, 482 separator, 483 {?__(83,"Flatten"),{flatten, 484 [{"X", x, ?__(84,"Flatten horizontally")}, 485 {"Y", y, ?__(85,"Flatten vertically")}]}, 486 ?__(86,"Flatten selected vertices")}, 487 {?__(87,"Tighten"),tighten, 488 ?__(88,"Move UV coordinates towards average midpoint"), 489 [magnet]}, 490 separator, 491 {?__(89,"Unfold"),{remap, lsqcm},?__(90,"Unfold the chart (without moving the selected vertices)")}, 492 {?__(91,"SphereMap"),sphere,?__(92,"Create a spherical mapping with " 493 "selected vertices being North/South pole")} 494 ] ++ option_menu(), 495 wings_menu:popup_menu(X,Y, {auv,vertex}, Menu); 496command_menu(_, X, Y) -> 497 case catch wpc_hlines:init() of 498 true -> ExportMenu = [separator, {auv_export_menu(label), export_uv, auv_export_menu(help)}]; 499 _ -> ExportMenu = [] 500 end, 501 Checked = [{crossmark, ?GET({?MODULE,show_background})}], 502 Menu = [{auv_show_menu(label),toggle_background,auv_show_menu(help),Checked}] ++ 503 ExportMenu ++ option_menu(), 504 wings_menu:popup_menu(X,Y, {auv,option}, Menu). 505 506stretch_directions() -> 507 [{?__(1,"Max Uniform"), max_uniform(), 508 {?__(2,"Maximize either horizontally or vertically"), 509 ?__(7,"Maximize by using the horizontal dimension"), 510 ?__(8,"Maximize by using the vertical dimension")}, []}, 511 {?__(3,"Max Horizontal"), max_x, ?__(4,"Maximize horizontally (X dir)")}, 512 {?__(5,"Max Vertical"), max_y, ?__(6,"Maximize vertically (Y dir)")}]. 513 514move_directions(true) -> 515 [{?__(1,"Free"), free_2d, ?__(2,"Move in both directions"), [magnet]}, 516 {?__(3,"Horizontal"), x, ?__(4,"Move horizontally (X dir)"), [magnet]}, 517 {?__(5,"Vertical"), y, ?__(6,"Move vertically (Y dir)"), [magnet]}]; 518move_directions(false) -> 519 [{?__(1,"Free"), free_2d, ?__(2,"Move in both directions")}, 520 {?__(3,"Horizontal"), x, ?__(4,"Move horizontally (X dir)")}, 521 {?__(5,"Vertical"), y, ?__(6,"Move vertically (Y dir)")}]. 522 523scale_directions(true) -> 524 [{?__(1,"Uniform"), uniform, ?__(2,"Scale in both directions"), [magnet]}, 525 {?__(3,"Horizontal"), x, ?__(4,"Scale horizontally (X dir)"), [magnet]}, 526 {?__(5,"Vertical"), y, ?__(6,"Scale vertically (Y dir)"), [magnet]}]; 527scale_directions(false) -> 528 [{?__(1,"Uniform"), uniform, ?__(2,"Scale in both directions"), []}, 529 {?__(3,"Horizontal"), x, ?__(4,"Scale horizontally (X dir)")}, 530 {?__(5,"Vertical"), y, ?__(6,"Scale vertically (Y dir)")}]. 531 532 533max_uniform() -> 534 fun 535 (1, _Ns) -> {auv,{scale,max_uniform}}; 536 (2, _Ns) -> {auv,{scale,{max_uniform,x}}}; 537 (3, _Ns) -> {auv,{scale,{max_uniform,y}}} 538 end. 539 540option_menu() -> 541 [separator, 542 {?__(1,"Create Texture"),create_texture,?__(2,"Make and Attach a texture to the model")}]. 543 544%%% Event handling 545 546get_event(#st{}=St) -> 547 wings_draw:refresh_dlists(St), 548 wings_wm:dirty(), 549 get_event_nodraw(St). 550 551get_event_nodraw(#st{}=St) -> 552 wings_wm:current_state(St), 553 {replace,fun(Ev) -> ?MODULE:handle_event(Ev, St) end}. 554 555handle_event({crash,Crash}, _) -> 556 wings_u:win_crash(Crash), 557 delete; 558handle_event({command_error,Error}, _) -> 559 wings_u:message(Error); 560handle_event(redraw, St) -> 561 redraw(St), 562 get_event_nodraw(St); 563handle_event(init_opengl, St) -> 564 wings:init_opengl(St), 565 get_event(St); 566handle_event(resized, St) -> 567 get_event(St); 568handle_event({new_state,St}, _) -> 569 new_state(St); 570handle_event(revert_state, St) -> 571 get_event(St); 572handle_event({current_state,geom_display_lists,GeomSt}, AuvSt) -> 573 new_geom_state(GeomSt, AuvSt); 574handle_event({do_tweak, Type, St =#st{sh=Sh,selmode=Mode}}, _) -> 575 case Type of 576 temp_selection -> 577 handle_command(move,St#st{temp_sel={Mode,Sh}}); 578 _ -> 579 handle_command(move,St) 580 end; 581handle_event({cancel_tweak,Ev}, St) -> 582 handle_event_1(Ev,St,wings_msg:free_lmb_modifier()); 583handle_event({add_faces,Fs,GeomSt}, St0) -> 584 AuvSt0 = add_faces(Fs,St0), 585 case update_geom_state(GeomSt, AuvSt0) of 586 {AuvSt,true} -> 587 wings_wm:send(geom, {new_state,GeomSt}), 588 new_state(AuvSt); 589 {AuvSt,false} -> 590 get_event(AuvSt) 591 end; 592handle_event(Ev, St) -> 593 case wings_camera:event(Ev, St) of 594 next -> 595 FreeLmbMod = wings_msg:free_lmb_modifier(), 596%% io:format("Ev ~W~n",[Ev,3]), 597 handle_event_0(Ev, St, FreeLmbMod); 598 Other -> 599 Other 600 end. 601 602%% Short cut for tweak like move 603handle_event_0(Ev=#mousebutton{state=?SDL_PRESSED, 604 x=X,y=Y, 605 button=?SDL_BUTTON_LEFT, 606 mod=Mod}, 607 #st{sel=Sel}=St0, FreeLmbMod) 608 when (Mod band 16#0FFF) == 0 -> %% No modifiers 609 case Sel of 610 [] -> 611 case wings_pick:do_pick(X, Y, St0) of 612 {add,_,St} -> 613 start_tweak(temp_selection, Ev, St); 614 _ -> 615 handle_event_1(Ev, St0, FreeLmbMod) 616 end; 617 _ -> 618 case wings_pick:do_pick(X,Y,St0) of 619 {delete,_,_} -> 620 start_tweak(selection, Ev, St0); 621 _ -> 622 handle_event_1(Ev, St0, FreeLmbMod) 623 end 624 end; 625handle_event_0(Ev, St, FreeLmbMod) -> 626 handle_event_1(Ev, St, FreeLmbMod). 627 628handle_event_1(Ev, St, _) -> 629 case wings_pick:event(Ev, St) of 630 next -> handle_event_2(Ev, St); 631 Other -> Other 632 end. 633 634handle_event_2(Ev, St) -> 635 case wings_hotkey:event(Ev, St) of 636 next -> handle_event_3(Ev, St); 637 Cmd -> wings_wm:later({action,Cmd}) 638 end. 639 640handle_event_3(#mousebutton{button=?SDL_BUTTON_RIGHT}=Ev, 641 #st{selmode=Mode0,sel=Sel}) -> 642 %% Note: Basic menus must be shown when the right mouse button 643 %% is PRESSED; advanced menus when the button is RELEASED. 644 %% wings_menu:is_popup_event/1 takes care of that. 645 case wings_menu:is_popup_event(Ev) of 646 no -> keep; 647 {yes,X,Y,_} -> 648 Mode = case Sel of 649 [] -> undefined; 650 _ -> Mode0 651 end, 652 command_menu(Mode, X, Y) 653 end; 654handle_event_3({drop,DropData}, St) -> 655 handle_drop(DropData, St); 656handle_event_3({action,{{auv,_},create_texture}}, St) -> 657 ?SET({?MODULE,show_background}, true), 658 auv_texture:draw_options(St); 659handle_event_3({action,{auv,{draw_options,restart}}}, St) -> 660 ?SET({?MODULE,show_background}, true), 661 auv_texture:draw_options(St); 662handle_event_3({action,{auv,{draw_options,Opt}}}, #st{bb=Uvs}=St) -> 663 #uvstate{st=GeomSt0,matname=MatName0,bg_img=Image} = Uvs, 664 Tx = ?SLOW(auv_texture:get_texture(St, Opt)), 665 case MatName0 of 666 none -> 667 ok = wings_image:update(Image, Tx), 668 ?SET({?MODULE,show_background}, true), 669 get_event(St); 670 _ -> 671 TexName = case get_texture(MatName0, St) of 672 false -> atom_to_list(MatName0); 673 Old -> 674 OldE3d = wings_image:info(Old), 675 case OldE3d#e3d_image.name of 676 "auvBG" -> atom_to_list(MatName0); 677 Other -> Other 678 end 679 end, 680 {GeomSt,MatName} = update_texture(Tx#e3d_image{name=TexName}, 681 MatName0, GeomSt0), 682 ImId = get_texture(MatName, GeomSt), 683 wings_wm:send(geom, {new_state,GeomSt}), 684 get_event(St#st{bb=Uvs#uvstate{bg_img=ImId, st=GeomSt,matname=MatName}}) 685 end; 686%% Others 687handle_event_3({vec_command,Command,_St}, _) when is_function(Command, 0) -> 688 %% Use to execute command with vector arguments (see wings_vec.erl). 689 Command(); 690handle_event_3(close, _St) -> 691 cleanup_before_exit(), 692 delete; 693handle_event_3({callback,Fun}, _) when is_function(Fun) -> 694 Fun(); 695handle_event_3({action,{auv,quit}}, _St) -> 696 cleanup_before_exit(), 697 delete; 698handle_event_3({action,{{auv,_},Cmd}}, St) -> 699 %% io:format("Cmd ~p ~n", [Cmd]), 700 handle_command(Cmd, St); 701handle_event_3({action,{auv,Cmd}}, St) -> 702 %% io:format("Cmd ~p ~n", [Cmd]), 703 handle_command(Cmd, St); 704handle_event_3({action,{select,show_all}}, #st{bb=#uvstate{st=GeomSt,id=Id}}) -> 705 wings_wm:send({autouv,Id}, {add_faces,object,GeomSt}), 706 keep; 707handle_event_3({action,{select,oriented_faces}}, St0) -> 708 Connected = wings_pref:get_value(similar_normals_connected,false), 709 {Save,Angle} = case wings_pref:get_value(similar_normals_angle,{false,1.0E-3}) of 710 {true,A} -> {true,A}; 711 {false,_} -> {false,1.0E-3} 712 end, 713 handle_event_3({action,{select,{oriented_faces,[Angle,Connected,Save]}}}, St0); 714handle_event_3({action,{select,similar_area}}, St0) -> 715 handle_event_3({action,{select,{similar_area,[0.001]}}}, St0); 716handle_event_3({action,{select,similar_material}}, St0) -> 717 Connected = wings_pref:get_value(similar_materials_connected, false), 718 Mode = wings_pref:get_value(similar_materials, material), 719 handle_event_3({action,{select,{similar_material,[Connected,Mode]}}}, St0); 720handle_event_3({action,{select,{ssels,sel_groups_win}}}, _) -> 721 keep; 722handle_event_3({action,{select,deselect_previous}=Command}, St0) -> 723 case wpc_deselect_previous:command(Command, St0) of 724 {save_state,St} -> ok; 725 _ -> St = St0 726 end, 727 new_state(St); 728handle_event_3({action,{select,Command}}, St0) -> 729 case wings_sel_cmd:command(Command, St0) of 730 {save_state,St} -> ok; 731 #st{}=St -> ok; 732 _ -> 733 %% That's avoid crash if any select option has a input dialog when 734 %% usually the returned value returned at the first time is 'keep'. 735 %% Also, for commands with preview dialog, the new state will not 736 %% be properly updated since somehow the local St seems to be messed 737 %% in {current_state,geom_display_lists,GeomSt} event handle 738 St = St0 739 end, 740 new_state(St); 741handle_event_3({action,{edit,repeat}}, St) -> 742 repeat(command, St); 743handle_event_3({action,{edit,repeat_args}}, St) -> 744 repeat(args, St); 745handle_event_3({action,{edit,repeat_drag}}, St) -> 746 repeat(drag, St); 747handle_event_3({action,Ev}=Act, #st{selmode=AUVSel, bb=#uvstate{st=#st{selmode=GSel}}}=St) -> 748 case Ev of %% Keyboard shortcuts end up here (I believe) 749 {_, {move,_}} -> 750 handle_command(move,St); 751 {_, {rotate,_}} -> 752 handle_command({rotate,free},St); 753 {_, {scale,{Dir,_S}}} -> 754 handle_command({scale,Dir},St); 755 {_, {scale,_S}} -> 756 handle_command({scale,uniform},St); 757 {_, slide} -> 758 handle_command(slide,St); 759 {_, circularise} -> 760 handle_command(circularise,St); 761 {view,{show,toggle_background}} -> 762 handle_command(toggle_background,St); 763 {view,aim} -> 764 St1 = fake_selection(St), 765 wings_view:command(aim, St1), 766 get_event(St); 767 {view,highlight_aim} -> 768 #st{sel=Sel} = St, 769 case Sel =:= [] of 770 true -> 771 St1 = fake_selection(St), 772 wings_view:command(aim, St1), 773 get_event(St); 774 false -> 775 {{_,Cmd},St1} = wings:highlight_aim_setup(St), 776 wings_view:command(Cmd,St1), 777 get_event(St) 778 end; 779 {view,Cmd} when Cmd == frame -> 780 wings_view:command(Cmd,St), 781 get_event(St); 782 {edit, repeat} -> 783 repeat(command, St); 784 {edit, repeat_args} -> 785 repeat(args, St); 786 {edit,repeat_drag} -> 787 repeat(drag, St); 788 _ when AUVSel =:= GSel -> 789 wings_wm:send_after_redraw(geom, Act), 790 keep; 791 {body, _} -> keep; 792 {face, _} -> keep; 793 {edge, _} -> keep; 794 {vertex,_} -> keep; 795 _ -> 796 wings_wm:send_after_redraw(geom, Act), 797 keep 798 end; 799handle_event_3(got_focus, _) -> 800 Msg1 = wings_msg:button_format(?__(1,"Select")), 801 Msg2 = wings_camera:help(), 802 Msg3 = wings_msg:button_format([], [], ?__(2,"Show menu")), 803 Message = wings_msg:join([Msg1,Msg2,Msg3]), 804 wings_wm:message(Message, ""), 805 auv_show_menu(true), 806 wings_wm:dirty(); 807handle_event_3(lost_focus, _) -> 808 auv_show_menu(false), 809 keep; 810handle_event_3(_Event, _) -> 811 %% io:format("MissEvent ~P~n", [_Event, 20]), 812 keep. 813 814clear_temp_sel(#st{temp_sel=none}=St) -> St; 815clear_temp_sel(#st{temp_sel={Mode,Sh}}=St) -> 816 St#st{temp_sel=none,selmode=Mode,sh=Sh,sel=[]}. 817 818-record(tweak, {type, st, pos, ev}). 819 820start_tweak(Type, Ev = #mousebutton{x=X,y=Y}, St0) -> 821 T = #tweak{type=Type,st=St0,pos={X,Y}, ev=Ev}, 822 {seq,push,get_tweak_event(T)}. 823 824get_tweak_event(T) -> 825 {replace,fun(Ev) -> tweak_event(Ev, T) end}. 826tweak_event(#mousemotion{x=X,y=Y}, #tweak{pos={Sx,Sy},type=Type,st=St}) -> 827 case (abs(X-Sx) > 2) orelse (abs(Y-Sy) > 2) of 828 true -> 829 if Type == temp_selection -> 830 wings_wm:later(clear_selection); 831 true -> ignore 832 end, 833 wings_wm:later({do_tweak,Type,St}), 834 pop; 835 false -> 836 keep 837 end; 838tweak_event(Other, #tweak{ev=Ev}) -> 839 wings_wm:later(Other), 840 wings_wm:later({cancel_tweak,Ev}), 841 pop. 842 843new_state(#st{bb=#uvstate{}=Uvs}=St0) -> 844 GeomSt = update_geom_selection(St0), 845 St1 = St0#st{bb=Uvs#uvstate{st=GeomSt}}, 846 St = update_selected_uvcoords(St1), 847 get_event(St). 848 849handle_command(Cmd, St0) -> 850 case handle_command_1(Cmd,remember_command(Cmd,St0)) of 851 Drag = {drag, _} -> do_drag(Drag); 852 Result -> Result 853 end. 854 855handle_ask(Cmd, St) -> 856%% io:format("Handle Ask ~p ~n",[Cmd]), 857 do_drag(handle_command_1(Cmd,St)). 858 859handle_command_1({'ASK',Ask}, St) -> 860 wings:ask(Ask, St, fun handle_ask/2); 861handle_command_1({remap,Method}, St0) -> 862 St = remap(Method, St0), 863 get_event(St); 864handle_command_1(move, St) -> 865 wings_move:setup(free_2d, St); 866handle_command_1({move,{'ASK',Ask}}, St) -> 867 wings:ask(Ask, St, fun(M,St0) -> 868 do_drag(wings_move:setup(M, St0)) 869 end); 870handle_command_1({move,Axis}, St) -> 871 wings_move:setup(Axis, St); 872handle_command_1({scale,Dir}, St0) %% Maximize chart 873 when Dir == max_uniform; Dir == {max_uniform,x}; Dir == {max_uniform,y}; 874 Dir == max_x; Dir == max_y -> 875 St1 = wpa:sel_map(fun(_, We) -> stretch(Dir,We) end, St0), 876 St = update_selected_uvcoords(St1), 877 get_event(St); 878handle_command_1({scale,normalize}, St0) -> %% Normalize chart sizes 879 #st{shapes=Sh0, bb=#uvstate{id=Id,st=#st{shapes=Orig}}} = St0, 880 OWe = gb_trees:get(Id, Orig), 881 {TA2D,TA3D,List} = wings_sel:fold(fun(_,We,Areas) -> 882 calc_areas(We,OWe,Areas) 883 end, {0.0,0.0,[]}, St0), 884 TScale = TA2D/TA3D, 885 Scale = fun({A2D,A3D,We0 = #we{id=WId}},Sh) -> 886 Scale = math:sqrt(TScale * A3D/A2D), 887 Center = wings_vertex:center(We0), 888 T0 = e3d_mat:translate(e3d_vec:neg(Center)), 889 SM = e3d_mat:scale(Scale, Scale, 1.0), 890 T1 = e3d_mat:mul(SM, T0), 891 T = e3d_mat:mul(e3d_mat:translate(Center), T1), 892 We = wings_we:transform_vs(T, We0), 893 gb_trees:update(WId,We,Sh) 894 end, 895 Sh = lists:foldl(Scale, Sh0, List), 896 St = update_selected_uvcoords(St0#st{shapes=Sh}), 897 get_event(St); 898handle_command_1({scale, {'ASK', Ask}}, St) -> 899 wings:ask(Ask, St, fun({Dir,M},St0) -> 900 do_drag(wings_scale:setup({Dir,center,M}, St0)) 901 end); 902handle_command_1({scale,{Dir,M}}, St) when element(1,M) == magnet -> 903 wings_scale:setup({Dir,center,M}, St); % For repeat drag 904handle_command_1({scale,Dir}, St) -> 905 wings_scale:setup({Dir,center}, St); 906handle_command_1(rotate, St) -> 907 wings_rotate:setup({free,center}, St); 908handle_command_1({rotate,free}, St) -> 909 wings_rotate:setup({free,center}, St); 910handle_command_1({rotate, {'ASK', Ask}}, St) -> 911 wings:ask(Ask, St, fun({Dir,M},St0) -> 912 do_drag(wings_rotate:setup({Dir,center,M}, St0)) 913 end); 914handle_command_1({rotate, Magnet}, St) when element(1, Magnet) == magnet -> 915 wings_rotate:setup({free,center,Magnet}, St); % For repeat drag 916handle_command_1({rotate,Dir}, St0) 917 when Dir == align_y; Dir == align_x; Dir == align_xy -> 918 St1 = align_chart(Dir, St0), 919 St = update_selected_uvcoords(St1), 920 get_event(St); 921handle_command_1({rotate,Deg}, St0) -> 922 St1 = wpa:sel_map(fun(_, We) -> rotate_chart(Deg, We) end, St0), 923 St = update_selected_uvcoords(St1), 924 get_event(St); 925handle_command_1({move_to,Dir}, St0) -> 926 St1 = wpa:sel_map(fun(_, We) -> move_to(Dir,We) end, St0), 927 St = update_selected_uvcoords(St1), 928 get_event(St); 929handle_command_1({align,Dir}, #st{selmode=Mode,sel=[{_,Els}]}=St0) -> 930 St = 931 case gb_sets:size(Els) of 932 1 -> 933 align_error(Mode), 934 St0; 935 _ -> 936 BB = wings_sel:bounding_box(St0), 937 St1 = wpa:sel_map(fun(_, We) -> align(Dir,BB,We) end, St0), 938 update_selected_uvcoords(St1) 939 end, 940 get_event(St); 941handle_command_1({flip,horizontal}, St0) -> 942 St1 = wpa:sel_map(fun(_, We) -> flip_horizontal(We) end, St0), 943 St = update_selected_uvcoords(St1), 944 get_event(St); 945handle_command_1({flip,vertical}, St0) -> 946 St1 = wpa:sel_map(fun(_, We) -> flip_vertical(We) end, St0), 947 St = update_selected_uvcoords(St1), 948 get_event(St); 949handle_command_1(slide, St) -> 950 wings_edge_cmd:command(slide,St); 951handle_command_1({equal,Op}, St0) -> 952 St1 = equal_length(Op,St0), 953 St = update_selected_uvcoords(St1), 954 get_event(St); 955handle_command_1(tighten, St) -> 956 tighten(St); 957handle_command_1({tighten,Magnet}, St) -> 958 tighten(Magnet, St); 959handle_command_1(delete, St) -> 960 get_event(delete_charts(St)); 961handle_command_1(hide, St) -> 962 get_event(hide_charts(St)); 963handle_command_1({flatten, Plane}, St0 = #st{selmode=vertex}) -> 964 {save_state, St1} = wings_vertex_cmd:flatten(Plane, St0), 965 St = update_selected_uvcoords(St1), 966 get_event(St); 967handle_command_1(stitch, St0 = #st{selmode=edge}) -> 968 Es = wpa:sel_fold(fun(Es,We=#we{name=#ch{emap=Emap},es=Etab},A) -> 969 Vis = gb_sets:from_list(wings_we:visible(We)), 970 [auv_segment:map_edge(E,Emap) 971 || E<-gb_sets:to_list(Es), 972 begin 973 #edge{lf=LF,rf=RF}=array:get(E, Etab), 974 border(gb_sets:is_member(LF,Vis), 975 gb_sets:is_member(RF,Vis)) 976 end] ++ A 977 end, [], St0), 978 St1 = stitch(lists:usort(Es),St0), 979 AuvSt = #st{bb=#uvstate{id=Id,st=Geom}} = update_selected_uvcoords(St1), 980 %% Do something here, i.e. restart uvmapper. 981 St = rebuild_charts(gb_trees:get(Id,Geom#st.shapes), AuvSt, []), 982 get_event(St); 983handle_command_1(cut_edges, St0 = #st{selmode=edge,bb=#uvstate{id=Id,st=Geom}}) -> 984 Es = wpa:sel_fold(fun(Es,We=#we{name=#ch{emap=Emap},es=Etab},A) -> 985 Vis = gb_sets:from_list(wings_we:visible(We)), 986 A ++ [auv_segment:map_edge(E,Emap) 987 || E <-gb_sets:to_list(Es), 988 begin 989 #edge{lf=LF,rf=RF} = array:get(E,Etab), 990 gb_sets:is_member(LF,Vis) and 991 gb_sets:is_member(RF,Vis) 992 end] 993 end, [], St0), 994 %% Do something here, i.e. restart uvmapper. 995 St1 = rebuild_charts(gb_trees:get(Id,Geom#st.shapes), St0, Es), 996 %% Displace charts some distance 997 St2 = displace_cuts(Es, St1), 998 St = update_selected_uvcoords(St2), 999 get_event(St); 1000handle_command_1(toggle_background, _) -> 1001 Old = ?GET({?MODULE,show_background}), 1002 ?SET({?MODULE,show_background},not Old), 1003 wings_wm:dirty(); 1004handle_command_1(export_uv, #st{}=St) -> 1005 wpc_hlines:command({file, {export_uv, {eps, true}}}, St); 1006handle_command_1(Cmd, #st{selmode=Mode}=St0) -> 1007 case wings_plugin:command({{auv,Mode},Cmd}, St0) of 1008 next -> 1009 io:format("Error unknown command ~p ~n", [Cmd]), 1010 keep; 1011 St0 -> St0; 1012 #st{}=St -> {save_state,St}; 1013 Other -> Other 1014 end. 1015 1016remember_command(Cmd,St0) -> 1017 St0#st{repeatable=Cmd,ask_args=none,drag_args=none}. 1018 1019repeat(_Type, #st{sel=Sel,repeatable=Rep}) when Sel == []; Rep == ignore -> 1020 keep; 1021repeat(Type, St=#st{selmode=Mode, repeatable=Cmd}) -> 1022%% io:format("Repeat ~p ~n", [{St#st.repeatable,St#st.ask_args,St#st.drag_args}]), 1023 case repeatable(Cmd,Mode) of 1024 false -> 1025%% io:format("Not repeatable ~p ~p ~p~n",[Type,Cmd,Mode]), 1026 keep; 1027 true -> 1028%% io:format("Repeatable ~p ~p ~p ~n",[Type,Cmd,Mode]), 1029 repeat(Type, Cmd, St) 1030 end. 1031 1032repeat(command, Cmd, St) -> 1033 repeat2(Cmd,St,none); 1034repeat(args, Cmd0, St = #st{ask_args=AskArgs}) -> 1035 Cmd =replace_ask(Cmd0, AskArgs), 1036 repeat2(Cmd,St,none); 1037repeat(drag, Cmd0, St = #st{ask_args=AskArgs,drag_args=DragArgs}) -> 1038 Cmd = replace_ask(Cmd0, AskArgs), 1039 repeat2(Cmd,St,DragArgs). 1040 1041repeat2(Cmd,St,DragArgs) -> 1042 case handle_command_1(Cmd,St) of 1043 {drag,Drag} -> 1044 wings_wm:set_prop(show_info_text, true), 1045 wings_drag:do_drag(Drag, DragArgs); 1046 Other -> Other 1047 end. 1048 1049replace_ask(Term, none) -> Term; 1050replace_ask({'ASK',_}, AskArgs) -> AskArgs; 1051replace_ask(Tuple0, AskArgs) when is_tuple(Tuple0) -> 1052 Tuple = [replace_ask(El, AskArgs) || El <- tuple_to_list(Tuple0)], 1053 list_to_tuple(Tuple); 1054replace_ask(Term, _) -> Term. 1055 1056repeatable({remap, proj_lsqcm}, Mode) -> Mode == face; 1057repeatable({remap, proj_slim}, Mode) -> Mode == face; 1058repeatable({remap,_},body) -> true; 1059repeatable({remap,_},Mode) -> Mode == vertex; 1060repeatable({scale, Dir}, Mode) 1061 when ((Dir == max_uniform) or (Dir == max_x) or (Dir == max_y) or 1062 (Dir == normalize)) and (Mode /= body) -> false; 1063repeatable({rotate, Dir}, Mode) 1064 when ((Dir == align_y) or (Dir == align_x) or (Dir == align_xy)) 1065 and ((Mode == body) or (Mode == face)) -> false; 1066repeatable({move_to,_},Mode) -> Mode == body; 1067repeatable({flip,_},Mode) -> Mode == body; 1068repeatable(slide, Mode) -> Mode == edge; 1069repeatable({equal,_}, Mode) -> Mode == edge; 1070repeatable(tighten, Mode) -> 1071 (Mode == vertex) orelse (Mode == body); 1072repeatable({tighten,_}, Mode) -> 1073 (Mode == vertex) orelse (Mode == body); 1074repeatable(delete, Mode) -> Mode == body; 1075repeatable(hide, Mode) -> Mode == body; 1076repeatable(flatten, Mode) -> Mode == edge; 1077repeatable(stitch, Mode) -> Mode == edge; 1078repeatable(cut_edges, Mode) -> Mode == edge; 1079repeatable(_Cmd,_Mode) -> 1080 true. 1081 1082fake_selection(St) -> 1083 wings_dl:fold(fun(#dlo{src_sel=none}, S) -> 1084 %% No selection, try highlighting. 1085 fake_sel_1(S); 1086 (#dlo{src_we=#we{id=Id},src_sel={Mode,Els}}, S) -> 1087 S#st{selmode=Mode,sel=[{Id,Els}]} 1088 end, St). 1089 1090fake_sel_1(St0) -> 1091 {_,X,Y} = wings_wm:local_mouse_state(), 1092 case wings_pick:do_pick(X, Y, St0) of 1093 {add,_,St} -> St; 1094 _ -> St0 1095 end. 1096 1097add_faces(NewFs,St0=#st{bb=ASt=#uvstate{id=Id,mode=Mode,st=GeomSt=#st{shapes=Shs0}}}) -> 1098 case {NewFs,Mode} of 1099 {_,object} -> 1100 All = wings_obj:fold(fun(#{id:=I}, A) -> [I|A] end, [], St0), 1101 wings_obj:unhide(All, St0); 1102 {object,_} -> %% Force a chart rebuild, we are switching object mode 1103 We = gb_trees:get(Id,Shs0), 1104 Shs = gb_trees:update(Id, We#we{fs=undefined,es=array:new()}, Shs0), 1105 Fake = GeomSt#st{sel=[],shapes=Shs}, 1106 St0#st{bb=ASt#uvstate{mode=object,st=Fake}}; 1107 {NewFs,Fs0} -> 1108 Fs = gb_sets:union(NewFs,Fs0), 1109 case gb_sets:intersection(NewFs,Fs0) of 1110 NewFs -> 1111 St0#st{bb=ASt#uvstate{mode=Fs}}; 1112 _ -> %% Some new faces should be shown, force a chart rebuild 1113 We = gb_trees:get(Id,Shs0), 1114 Shs = gb_trees:update(Id, We#we{fs=undefined,es=array:new()}, Shs0), 1115 Fake = GeomSt#st{sel=[],shapes=Shs}, 1116 St0#st{bb=ASt#uvstate{st=Fake,mode=Fs}} 1117 end 1118 end. 1119 1120do_drag({drag,Drag}) -> 1121 wings:mode_restriction([vertex,edge,face,body]), 1122 wings_wm:set_prop(show_info_text, true), 1123 wings_drag:do_drag(Drag,none); 1124do_drag(Other) -> 1125 Other. 1126 1127tighten(#st{selmode=vertex}=St) -> 1128 tighten_1(fun vertex_tighten/2, St); 1129tighten(#st{selmode=body}=St) -> 1130 tighten_1(fun(_, We) -> body_tighten(We) end, St). 1131 1132tighten_1(Tighten, St) -> 1133 wings_drag:fold(Tighten, [percent], St). 1134 1135vertex_tighten(Vs0, We) -> 1136 Vis = gb_sets:from_ordset(wings_we:visible(We)), 1137 Vs = [V || V <- gb_sets:to_list(Vs0), not_bordering(V, Vis, We)], 1138 wings_vertex_cmd:tighten_vs(Vs, We). 1139 1140body_tighten(#we{vp=Vtab}=We) -> 1141 Vis = gb_sets:from_ordset(wings_we:visible(We)), 1142 Vs = [V || V <- wings_util:array_keys(Vtab), not_bordering(V, Vis, We)], 1143 wings_vertex_cmd:tighten_vs(Vs, We). 1144 1145tighten(Magnet, St) -> 1146 Flags = wings_magnet:flags(Magnet, []), 1147 wings_drag:fold(fun(Vs, We) -> 1148 mag_vertex_tighten(Vs, We, Magnet) 1149 end, [percent,falloff], Flags, St). 1150 1151mag_vertex_tighten(Vs0, We, Magnet) -> 1152 Vis = gb_sets:from_ordset(wings_we:visible(We)), 1153 Vs = [V || V <- gb_sets:to_list(Vs0), not_bordering(V, Vis, We)], 1154 wings_vertex_cmd:tighten_vs(Vs, We, Magnet). 1155 1156equal_length(Op,St) -> 1157 wings_sel:map(fun(Es, We) -> 1158 Links = wings_edge_loop:edge_links(Es,We), 1159 make_equal(Op,Links,We) 1160 end, St). 1161 1162make_equal(Op,[Link0|R],We = #we{vp=Vtab}) -> 1163 E = if Op == horizontal -> 1; true -> 2 end, 1164 case length(Link0) of 1165 X when X < 2 -> make_equal(Op,R,We); 1166 No -> 1167 Link = case Link0 of 1168 [{_,A,_},{_,_,A}|_] -> Link0; 1169 [{_,_,A},{_,A,_}|_] -> reverse(Link0) 1170 end, 1171 D = foldl(fun({_E,Ve,Vs}, {X,Y,_}) -> 1172 {Xd,Yd,_} = e3d_vec:sub(array:get(Ve,Vtab), 1173 array:get(Vs,Vtab)), 1174 {X+(Xd),Y+(Yd),0.0} 1175 end, 1176 {0.0,0.0,0.0},Link), 1177 Dist = element(E,D)/No, 1178 Vt = foldl(fun({_E,Ve,Vs}, Vt) -> 1179 Pos1 = array:get(Vs,Vt), 1180 Pos2 = array:get(Ve,Vt), 1181 Pos = setelement(E,Pos2,element(E,Pos1) + Dist), 1182 array:set(Ve,Pos,Vt) 1183 end, 1184 Vtab,Link), 1185 make_equal(Op,R,We#we{vp=Vt}) 1186 end; 1187make_equal(_,[],We) -> We. 1188 1189calc_areas(We,OWe,{TA2D,TA3D,L}) -> 1190 Fs = wings_we:visible(We), 1191 {A2D,A3D} = 1192 lists:foldl(fun(Face,{A2D,A3D}) -> 1193 %% 2D 1194 Vs2 = wpa:face_vertices(Face, We), 1195 A2 = auv_mapping:calc_area(Vs2,{0.0,0.0,1.0}, We), 1196 %% 3D 1197 N3 = wings_face:normal(Face, OWe), 1198 Vs3 = wpa:face_vertices(Face, OWe), 1199 A3 = auv_mapping:calc_area(Vs3,N3, OWe), 1200 {A2D+A2,A3+A3D} 1201 end, {0.0,0.0}, Fs), 1202 {A2D+TA2D,A3D+TA3D,[{A2D,A3D,We}|L]}. 1203 1204not_bordering(V, Vis, We) -> 1205 wings_vertex:fold(fun(_, _, _, false) -> false; 1206 (_, F, _, true) -> gb_sets:is_member(F, Vis) 1207 end, true, V, We). 1208 1209hide_charts(#st{shapes=Shs0,bb=UVs}=St) -> 1210 Shs = wpa:sel_fold(fun(_, #we{id=Id}, Shs) -> 1211 gb_trees:delete(Id, Shs) 1212 end, Shs0, St), 1213 Fs = foldl(fun(#we{fs=Ftab},Acc) -> 1214 Fs = gb_sets:from_ordset(gb_trees:keys(Ftab)), 1215 gb_sets:union(Fs,Acc) 1216 end, gb_sets:empty(), gb_trees:values(Shs)), 1217 St#st{shapes=Shs,sel=[],bb=UVs#uvstate{mode=Fs}}. 1218 1219delete_charts(#st{shapes=Shs0}=St0) -> 1220 St1 = wpa:sel_map(fun(_, #we{vp=Vp0}=We) -> 1221 Vp1 = array:sparse_to_orddict(Vp0), 1222 Vp = [{V,none} || {V,_} <- Vp1], 1223 We#we{vp=array:from_orddict(Vp)} 1224 end, St0), 1225 St = update_selected_uvcoords(St1), 1226 Shs = wpa:sel_fold(fun(_, #we{id=Id}, Shs) -> 1227 gb_trees:delete(Id, Shs) 1228 end, Shs0, St), 1229 St#st{shapes=Shs,sel=[]}. 1230 1231border(false,true) -> true; 1232border(true,false) -> true; 1233border(_,_) -> false. 1234 1235stitch(WEs,St0) -> 1236 Mapped0 = map_edges(WEs,St0), 1237 %% 1st pass take care and remove all chart-internal cuts. 1238 {Mapped1,St1} = stitch_edges(Mapped0, St0, []), 1239 %% Cluster all edges between 2 charts together 1240 ChartStitches = cluster_chart_moves(lists:keysort(2,Mapped1),[]), 1241 %% 2nd pass take care of chart stitches 1242 stitch_charts(ChartStitches,gb_sets:empty(),St1). 1243 1244stitch_edges([{_E,{Id,_,{Vs1,Ve1}},{Id,_,{Vs2,Ve2}}}|Rest], 1245 St0=#st{shapes=Sh0},Acc) -> 1246 %% Same We do the internal stiches 1247 We = #we{vp=Vpos0} = gb_trees:get(Id,Sh0), 1248 Same = [{Vs1,Vs2},{Ve1,Ve2}], 1249 Vpos = average_pos(Same, Vpos0), 1250 St = St0#st{shapes = gb_trees:update(Id, We#we{vp=Vpos}, Sh0)}, 1251 stitch_edges(Rest,St,Acc); 1252stitch_edges([Other|Rest], St, Acc) -> 1253 stitch_edges(Rest,St,[Other|Acc]); 1254stitch_edges([],St,Acc) -> {Acc,St}. 1255 1256stitch_charts([],_,St0) -> St0; 1257stitch_charts([ChartStitches|Other],Moved,St0=#st{shapes=Sh0}) -> 1258 {Id1,Id2,{Vs1,Ve1,Vs2,Ve2}} = find_longest_dist(ChartStitches, St0), 1259 We1_0 = #we{vp=Vpos1}=gb_trees:get(Id1,Sh0), 1260 We2_0 = #we{vp=Vpos2}=gb_trees:get(Id2,Sh0), 1261 Vs1P = array:get(Vs1,Vpos1),Ve1P = array:get(Ve1,Vpos1), 1262 Vs2P = array:get(Vs2,Vpos2),Ve2P = array:get(Ve2,Vpos2), 1263 C1 = e3d_vec:average(Vs1P,Ve1P), 1264 C2 = e3d_vec:average(Vs2P,Ve2P), 1265 Sh = case {gb_sets:is_member(Id2,Moved),gb_sets:is_member(Id2,Moved)} of 1266 {false,_} -> 1267 Dist = e3d_vec:sub(C1,C2), 1268 Deg = (x_rad(Vs2P,Ve2P) - x_rad(Vs1P,Ve1P)) * 180.0/math:pi(), 1269 We2_1 = rotate_chart(-Deg,C2,We2_0), 1270 T = e3d_mat:translate(Dist), 1271 We2 = wings_we:transform_vs(T, We2_1), 1272 gb_trees:update(Id2, We2, Sh0); 1273 {_,false} -> 1274 Dist = e3d_vec:sub(C2,C1), 1275 Deg = (x_rad(Vs1P,Ve1P)-x_rad(Vs2P,Ve2P)) * 180.0/math:pi(), 1276 We1_1 = rotate_chart(-Deg,C1,We1_0), 1277 T = e3d_mat:translate(Dist), 1278 We1 = wings_we:transform_vs(T, We1_1), 1279 gb_trees:update(Id1, We1, Sh0); 1280 _ -> 1281 wings_u:error_msg(?__(1,"Hmm, I can't stitch so many charts at the same time")) 1282 end, 1283 St = foldl(fun stitch_charts2/2, St0#st{shapes=Sh}, ChartStitches), 1284 stitch_charts(Other, gb_sets:add(Id2,gb_sets:add(Id1,Moved)), St). 1285 1286stitch_charts2({_E,{Id1,E1,{Vs1,Ve1}},{Id2,E2,{Vs2,Ve2}}}, 1287 St0=#st{shapes=Sh0,sel=Sel}) -> 1288 We1 = #we{vp=Vpos1} = gb_trees:get(Id1,Sh0), 1289 We2 = #we{vp=Vpos2} = gb_trees:get(Id2,Sh0), 1290 Same = [{Vs1,Vs2},{Ve1,Ve2}], 1291 {Vp1,Vp2} = average_pos(Same, Vpos1, Vpos2), 1292 Sh1 = gb_trees:update(Id1, We1#we{vp=Vp1}, Sh0), 1293 Sh = gb_trees:update(Id2, We2#we{vp=Vp2}, Sh1), 1294 St0#st{shapes = Sh, sel=add_sel([{Id1,E1},{Id2,E2}],Sel)}. 1295 1296add_sel([{Id,Edge}|R],Sel) -> 1297 case lists:keysearch(Id,1,Sel) of 1298 {value, {Id,Set}} -> 1299 add_sel(R, lists:keyreplace(Id, 1, Sel, {Id,gb_sets:add(Edge,Set)})); 1300 false -> 1301 add_sel(R, [{Id,gb_sets:singleton(Edge)}|Sel]) 1302 end; 1303add_sel([],Sel) -> Sel. 1304 1305x_rad({X1,Y1,_},{X2,Y2,_}) -> 1306 Rad = math:atan2(Y2-Y1,X2-X1), 1307 if Rad < 0.0 -> 2*math:pi()+Rad; 1308 true -> Rad 1309 end. 1310 1311find_longest_dist([{_,{Id1,_,{Vs1,Ve1}},{Id2,_,{Vs2,Ve2}}}|Rest],#st{shapes=Sh}) -> 1312 %% Need to find a vector to use as basis to rotate the other chart to 1313 %% we grab the longest distance between to verts of chart1 1314 #we{vp=Vpos} = gb_trees:get(Id1,Sh), 1315 Dist = e3d_vec:dist(array:get(Vs1,Vpos),array:get(Ve1,Vpos)), 1316 {Id1,Id2,find_longest_dist(Rest,Dist,Vs1,Ve1,Vs2,Ve2,Vpos)}. 1317 1318find_longest_dist([],_Dist,Bs1,Be1,Bs2,Be2,_Vpos) -> {Bs1,Be1,Bs2,Be2}; 1319find_longest_dist([This|Rest],Dist,Bs1,Be1,Bs2,Be2,Vpos) -> 1320 {_,{_,_,{Vs1,Ve1}},{_,_,{Vs2,Ve2}}} = This, 1321 Dist1 = e3d_vec:dist(array:get(Vs1,Vpos),array:get(Be1,Vpos)), 1322 Dist2 = e3d_vec:dist(array:get(Vs1,Vpos),array:get(Bs1,Vpos)), 1323 Dist3 = e3d_vec:dist(array:get(Ve1,Vpos),array:get(Be1,Vpos)), 1324 Dist4 = e3d_vec:dist(array:get(Ve1,Vpos),array:get(Bs1,Vpos)), 1325 if (Dist1>Dist),(Dist1>Dist2),(Dist1>Dist3),(Dist1>Dist4) -> 1326 find_longest_dist(Rest,Dist1,Vs1,Be1,Vs2,Be2,Vpos); 1327 (Dist2>Dist),(Dist2>Dist3),(Dist2>Dist4) -> 1328 find_longest_dist(Rest,Dist2,Vs1,Bs1,Vs2,Bs2,Vpos); 1329 (Dist3>Dist),(Dist3>Dist4) -> 1330 find_longest_dist(Rest,Dist3,Ve1,Be1,Ve2,Be2,Vpos); 1331 (Dist4>Dist) -> 1332 find_longest_dist(Rest,Dist4,Ve1,Bs1,Ve2,Bs2,Vpos); 1333 true -> 1334 find_longest_dist(Rest,Dist,Bs1,Be1,Bs2,Be2,Vpos) 1335 end. 1336 1337cluster_chart_moves([EM={_,{Id1,_,_},{Id2,_,_}}|R], Acc) -> 1338 {Same,Rest} = cluster_chart_moves2(Id1,Id2,R,[EM],[]), 1339 cluster_chart_moves(Rest,[Same|Acc]); 1340cluster_chart_moves([],Acc) -> Acc. 1341 1342cluster_chart_moves2(Id1,Id2,[EM={_,{Id1,_,_},{Id2,_,_}}|R],Same,Other) -> 1343 cluster_chart_moves2(Id1,Id2,R,[EM|Same],Other); 1344cluster_chart_moves2(Id1,Id2,[EM={_,{Id1,_,_},_}|R],Same,Other) -> 1345 cluster_chart_moves2(Id1,Id2,R,Same,[EM|Other]); 1346cluster_chart_moves2(_Id1,_Id2,R,Same,Other) -> 1347 {Same,Other++R}. 1348 1349average_pos([{V1,V2}|R], Vpos0) -> 1350 Pos = e3d_vec:average(array:get(V1,Vpos0),array:get(V2,Vpos0)), 1351 Vpos1 = array:set(V1,Pos,Vpos0), 1352 Vpos = array:set(V2,Pos,Vpos1), 1353 average_pos(R, Vpos); 1354average_pos([],Vpos) -> Vpos. 1355 1356average_pos([{V1,V2}|R], Vpos1,Vpos2) -> 1357 Pos = e3d_vec:average(array:get(V1,Vpos1),array:get(V2,Vpos2)), 1358 Vp1 = array:set(V1,Pos,Vpos1), 1359 Vp2 = array:set(V2,Pos,Vpos2), 1360 average_pos(R, Vp1,Vp2); 1361average_pos([],Vpos1,Vpos2) -> {Vpos1,Vpos2}. 1362 1363displace_cuts(SelEs,St=#st{shapes=Sh,bb=#uvstate{id=WeId,st=#st{shapes=GSh}}})-> 1364 Elinks = wings_edge_loop:partition_edges(SelEs,gb_trees:get(WeId,GSh)), 1365 Remapped = [map_edges(Elink,St) || Elink <- Elinks], 1366 %% Remapped = [[{GeomEdge, [{AuvWeId,AuvEdge1},{AuvWeId2,AuvEdge2}]},..] 1367 Displaced = foldl(fun displace_cuts1/2, Sh, Remapped), 1368 %% Update selection to all new edges 1369 Sel0 = lists:append([[{Id1,E1},{Id2,E2}] || 1370 {_,{Id1,E1,_},{Id2,E2,_}} 1371 <- lists:append(Remapped)]), 1372 Sel1 = sofs:to_external(sofs:relation_to_family( 1373 sofs:relation(Sel0))), 1374 Sel = [{Id,gb_sets:from_ordset(Eds)} || {Id,Eds} <- Sel1], 1375 St#st{sel=Sel,shapes=Displaced}. 1376 1377displace_cuts1(Eds, Sh0) -> 1378 Sh1 = displace_edges(Eds,Sh0), 1379 displace_charts(Eds,gb_sets:empty(),Sh1). 1380 1381displace_edges([{_,{Id,Edge1,{Vs1,Ve1}},{Id,_,{Vs2,Ve2}}}|Eds], Sh) -> 1382 We = #we{vp=Vpos0} = gb_trees:get(Id,Sh), 1383 %% Get the vertices 1384 Same = [{Vs1,Vs2},{Ve1,Ve2}], 1385 Vs = [{V1,V2} || {V1,V2} <- Same, 1386 V1 /= V2, 1387 array:get(V1,Vpos0) == array:get(V2,Vpos0)], 1388 case Vs of 1389 [] -> %% Already displaced 1390 displace_edges(Eds,Sh); 1391 _ -> 1392 %% What Direction should we displace the verts? 1393 [Move1,Move2] = displace_dirs(0.0000001,Edge1,We), 1394 %% Make the move 1395 Vpos = foldl(fun({V1,V2},VpIn) -> 1396 Pos1 = e3d_vec:add(Move1,array:get(V1,Vpos0)), 1397 Vpos1 = array:set(V1,Pos1,VpIn), 1398 Pos2 = e3d_vec:add(Move2,array:get(V2,Vpos0)), 1399 array:set(V2,Pos2,Vpos1) 1400 end, Vpos0, Vs), 1401 displace_edges(Eds,gb_trees:update(Id, We#we{vp=Vpos},Sh)) 1402 end; 1403displace_edges([_Skip|Eds], Sh) -> 1404 displace_edges(Eds,Sh); 1405displace_edges([],Sh) -> Sh. 1406 1407displace_charts([],_,Sh) -> Sh; 1408displace_charts([{_,{Id,_,_},{Id,_,_}}|Eds],Moved,Sh) -> 1409 displace_charts(Eds,Moved,Sh); 1410displace_charts([{_,{Id1,_,_},{Id2,_,_}}|Eds], Moved, Sh) -> 1411 case gb_sets:is_member(Id1,Moved) or gb_sets:is_member(Id2,Moved) of 1412 true -> displace_charts(Eds,Moved,Sh); 1413 false -> 1414 We0 = #we{vp=Vpos0} = gb_trees:get(Id1,Sh), 1415 C1 = wings_vertex:center(We0), 1416 C2 = wings_vertex:center(gb_trees:get(Id2,Sh)), 1417 Disp0 = e3d_vec:mul(e3d_vec:norm(e3d_vec:sub(C1,C2)),0.0000001), 1418 Move = case Disp0 of 1419 {0.0,0.0,0.0} -> {0.0,0.0000001,0.0}; 1420 Disp -> Disp 1421 end, 1422 Vpos= [{V,e3d_vec:add(Pos,Move)} || 1423 {V,Pos} <- array:sparse_to_orddict(Vpos0)], 1424 We = We0#we{vp=array:from_orddict(Vpos)}, 1425 displace_charts(Eds,gb_sets:add(Id1,Moved), 1426 gb_trees:update(Id1,We,Sh)) 1427 end. 1428 1429displace_dirs(Dist,Edge1,We = #we{es=Etab,vp=Vpos}) -> 1430 #edge{vs=Vs1,ve=Ve1,lf=LF,rf=RF} = array:get(Edge1,Etab), 1431 Vp1 = array:get(Vs1,Vpos), 1432 Vp2 = array:get(Ve1,Vpos), 1433 {Dx,Dy,_} = e3d_vec:norm(e3d_vec:sub(Vp1,Vp2)), 1434 Dir = {Dy,-Dx,0.0}, 1435 EdgeFace1 = if LF < 0 -> RF; true -> LF end, 1436 FaceCenter = wings_face:center(EdgeFace1, We), 1437 FaceDir = e3d_vec:sub(e3d_vec:average(Vp1,Vp2),FaceCenter), 1438 Moves = [e3d_vec:mul(Dir,-Dist),e3d_vec:mul(Dir,Dist)], 1439 case e3d_vec:dot(FaceDir,Dir) > 0.0 of 1440 true -> Moves; 1441 false -> reverse(Moves) 1442 end. 1443 1444%% Create a mapping from wings_edges to autouv edges. 1445%% Res is [{GeomEdge, {AuvWeId,AuvEdge1,{E1Vs,E1Ve}},{AuvWeId2,AuvEdge2,..}},..] 1446map_edges(WingsEs,#st{shapes=Sh}) -> 1447 AuvEds = edge_sel_to_edge(gb_trees:to_list(Sh),WingsEs,[]), 1448 %% AuvEds = [{id,auv_eds},..], [{id,auv_eds},..] 1449 MapEds = fun({Id,Es},A) -> 1450 #we{name=#ch{emap=Emap}} = gb_trees:get(Id,Sh), 1451 [{auv_segment:map_edge(E,Emap),{Id,E}} 1452 || E<-gb_sets:to_list(Es)] ++ A 1453 end, 1454 Mapped0 = foldl(MapEds, [], AuvEds), 1455 Mapped = sofs:to_external(sofs:relation_to_family(sofs:relation(Mapped0))), 1456 foldl( 1457 fun(_L={E,[{Id1,E1},{Id2,E2}]},Acc) -> 1458 #we{name=#ch{vmap=Vmap1},es=Etab1}=gb_trees:get(Id1,Sh), 1459 #we{name=#ch{vmap=Vmap2},es=Etab2}=gb_trees:get(Id2,Sh), 1460 #edge{vs=Vs1,ve=Ve1} = array:get(E1,Etab1), 1461 #edge{vs=Vs2,ve=Ve2} = array:get(E2,Etab2), 1462 Vs1map = auv_segment:map_vertex(Vs1, Vmap1), 1463 R= case auv_segment:map_vertex(Vs2, Vmap2) of 1464 Vs1map -> {E,{Id1,E1,{Vs1,Ve1}},{Id2,E2,{Vs2,Ve2}}}; 1465 _ -> {E,{Id1,E1,{Vs1,Ve1}},{Id2,E2,{Ve2,Vs2}}} 1466 end, 1467 [R|Acc]; 1468 (_,Acc) -> Acc 1469 end, [], Mapped). 1470 1471%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1472drag_filter({image,_,_}) -> 1473 {yes,?__(1,"Drop: Change the texture image")}; 1474drag_filter(_) -> no. 1475 1476handle_drop(#{type:=image,id:=Id}, #st{bb=Uvs0}=St) -> 1477 Uvs = Uvs0#uvstate{bg_img=Id}, 1478 get_event(St#st{bb=Uvs}); 1479handle_drop(_DropData, _) -> 1480 ?dbg("Ignore ~P~n",[_DropData,30]), 1481 keep. 1482 1483%% is_power_of_two(X) -> 1484%% (X band -X ) == X. 1485 1486%%% 1487%%% Update charts from new state of Geometry window. 1488%%% 1489 1490new_geom_state(GeomSt, AuvSt0) -> 1491 case update_geom_state(GeomSt, AuvSt0) of 1492 {AuvSt,true} -> get_event(AuvSt); 1493 {AuvSt,false} -> get_event_nodraw(AuvSt); 1494 delete -> 1495 cleanup_before_exit(), 1496 delete 1497 end. 1498 1499update_geom_state(#st{mat=Mat,shapes=Shs}=GeomSt, AuvSt0) -> 1500 case new_geom_state_0(Shs, Mat, AuvSt0) of 1501 {AuvSt1,ForceRefresh0} -> 1502 {AuvSt,ForceRefresh1} = update_selection(GeomSt, AuvSt1), 1503 {AuvSt,ForceRefresh0 or ForceRefresh1}; 1504 Other -> Other %% delete 1505 end. 1506 1507new_geom_state_0(Shs, Mat, #st{bb=#uvstate{matname=none}}=AuvSt) -> 1508 new_geom_state_1(Shs, AuvSt#st{mat=Mat}); 1509new_geom_state_0(Shs, Mtab0, #st{bb=#uvstate{matname=MatName}=BB, mat=Mtab1}=AuvSt) -> 1510 case {get_texture(MatName, Mtab0), get_texture(MatName, Mtab1)} of 1511 {Same,Same} -> 1512 new_geom_state_1(Shs, AuvSt#st{mat=Mtab0}); 1513 {New, _} when New =/= false -> 1514 new_geom_state_1(Shs, AuvSt#st{mat=Mtab0, bb=BB#uvstate{bg_img=New}}); 1515 _ -> 1516 new_geom_state_1(Shs, AuvSt#st{mat=Mtab0}) 1517 end. 1518 1519new_geom_state_1(Shs, #st{bb=#uvstate{id=Id,st=#st{shapes=Orig}}}=AuvSt) -> 1520 case {gb_trees:lookup(Id, Shs),gb_trees:lookup(Id, Orig)} of 1521 {none,_} -> delete; 1522 {{value,We},{value,We}} -> {AuvSt,false}; 1523 {{value,#we{es=Etab}=We},{value,#we{es=Etab}=OldWe}} -> 1524 case wings_va:any_update(We, OldWe) of 1525 false -> {AuvSt,false}; 1526 true -> {rebuild_charts(We, AuvSt, []),true} 1527 end; 1528 {{value,We},_} -> {rebuild_charts(We, AuvSt, []),true} 1529 end. 1530 1531rebuild_charts(We, St = #st{bb=UVS=#uvstate{st=Old,mode=Mode}}, ExtraCuts) -> 1532 {Faces,FvUvMap} = auv_segment:fv_to_uv_map(Mode,We), 1533 {Charts0,Cuts0} = auv_segment:uv_to_charts(Faces, FvUvMap, We), 1534 {Charts1,Cuts} = 1535 case ExtraCuts of 1536 [] -> {Charts0,Cuts0}; 1537 _ -> 1538 Cuts1 = gb_sets:union(Cuts0, gb_sets:from_list(ExtraCuts)), 1539 auv_segment:normalize_charts(Charts0, Cuts1, We) 1540 end, 1541 Charts2 = auv_segment:cut_model(Charts1, Cuts, We), 1542 Charts = update_uv_tab(Charts2, FvUvMap), 1543 wings_wm:set_prop(wireframed_objects,gb_sets:from_list(gb_trees:keys(Charts))), 1544 St#st{sel=[],bb=UVS#uvstate{mode=update_mode(Faces,We),st=Old#st{sel=[]}}, 1545 shapes=Charts}. 1546 1547update_mode(Faces0, #we{fs=Ftab}) -> 1548 Fs = gb_sets:from_list(Faces0), 1549 case gb_sets:size(Fs) == gb_trees:size(Ftab) of 1550 true -> object; 1551 false -> Fs 1552 end. 1553 1554update_uv_tab(Cs, FvUvMap) -> 1555 update_uv_tab_1(Cs, FvUvMap, []). 1556 1557update_uv_tab_1([#we{id=Id,name=#ch{vmap=Vmap}}=We0|Cs], FvUvMap, Acc) -> 1558 Fs = wings_we:visible(We0), 1559 UVs0 = wings_face:fold_faces( 1560 fun(F, V, _, _, A) -> 1561 OrigV = auv_segment:map_vertex(V, Vmap), 1562 [{V,[F|OrigV]}|A] 1563 end, [], Fs, We0), 1564 case update_uv_tab_2(sort(UVs0), FvUvMap, 0.0, []) of 1565 error -> 1566 %% No UV coordinate for at least some vertices (probably 1567 %% all) in the chart. Throw away this chart. 1568 update_uv_tab_1(Cs, FvUvMap, Acc); 1569 UVs1 -> 1570 UVs = array:from_orddict(UVs1), 1571 We = We0#we{vp=UVs}, 1572 update_uv_tab_1(Cs, FvUvMap, [{Id,We}|Acc]) 1573 end; 1574update_uv_tab_1([], _, Acc) -> 1575 gb_trees:from_orddict(sort(Acc)). 1576 1577update_uv_tab_2([{V,_}|T], FvUvMap, Z, [{V,_}|_]=Acc) -> 1578 update_uv_tab_2(T, FvUvMap, Z, Acc); 1579update_uv_tab_2([{V,Key}|T], FvUvMap, Z, Acc) -> 1580 case gb_trees:get(Key, FvUvMap) of 1581 {X,Y} -> 1582 Pos = {X,Y,Z}, 1583 update_uv_tab_2(T, FvUvMap, Z, [{V,Pos}|Acc]); 1584 _ -> 1585 %% No UV-coordinate for this vertex. Abandon the entire chart. 1586 error 1587 end; 1588update_uv_tab_2([], _, _, Acc) -> reverse(Acc). 1589 1590%% update_selection(GemoSt, AuvSt0) -> AuvSt 1591%% Update the selection in the AutoUV window given a selection 1592%% from a geometry window. 1593update_selection(#st{selmode=Mode,sel=Sel}=St, 1594 #st{bb=#uvstate{st=#st{selmode=Mode,sel=Sel}}=Uvs}=AuvSt) -> 1595 {AuvSt#st{bb=Uvs#uvstate{st=St}},false}; 1596update_selection(#st{selmode=Mode,sel=Sel}=St0, 1597 #st{selmode=AuvMode,bb=#uvstate{id=Id}=Uvs, 1598 shapes=Charts0}=AuvSt0) -> 1599 Charts = gb_trees:to_list(Charts0), 1600 {case keysearch(Id, 1, Sel) of 1601 false -> 1602 %% No selection in any chart - clear selection. 1603 AuvSt0#st{sel=[],bb=Uvs#uvstate{st=St0}}; 1604 {value,{Id,Elems0}} when AuvMode == body -> 1605 %% Body selection in charts - must be specially handled. 1606 Elems = gb_sets:to_list(Elems0), 1607 NewSel = update_body_sel(Mode, Elems, Charts), 1608 AuvSt0#st{sel=sort(NewSel),sh=false,bb=Uvs#uvstate{st=St0}}; 1609 {value,{Id,Elems0}} when AuvMode =:= Mode-> 1610 %% Same selection mode in Geometry and AutoUV. 1611 Elems = gb_sets:to_list(Elems0), 1612 NewSel = update_selection_1(AuvMode, Elems, Charts), 1613 AuvSt0#st{sel=sort(NewSel),sh=false,bb=Uvs#uvstate{st=St0}}; 1614 {value,IdElems} -> 1615 %% Different selection modes. Convert Geom selection to 1616 %% the mode in AutoUV. 1617 St = St0#st{sel=[IdElems]}, 1618 #st{sel=[{Id,Elems0}]} = wings_sel_conv:mode(AuvMode, St), 1619 Elems = gb_sets:to_list(Elems0), 1620 NewSel = update_selection_1(AuvMode, Elems, Charts), 1621 AuvSt0#st{sel=sort(NewSel),sh=false,bb=Uvs#uvstate{st=St0}} 1622 end,true}. 1623 1624update_selection_1(vertex, Vs, Charts) -> 1625 vertex_sel_to_vertex(Charts, Vs, []); 1626update_selection_1(edge, Es, Charts) -> 1627 edge_sel_to_edge(Charts, Es, []); 1628update_selection_1(face, Faces, Charts) -> 1629 face_sel_to_face(Charts, Faces, []). 1630 1631face_sel_to_face([{K,We}|Cs], Faces, Sel) -> 1632 ChartFaces = auv2geom_faces(wings_we:visible(We), We), 1633 case ordsets:intersection(ChartFaces, Faces) of 1634 [] -> 1635 face_sel_to_face(Cs, Faces, Sel); 1636 FaceSel0 -> 1637 FaceSel1 = geom2auv_faces(FaceSel0, We), 1638 FaceSel = gb_sets:from_list(FaceSel1), 1639 face_sel_to_face(Cs, Faces, [{K,FaceSel}|Sel]) 1640 end; 1641face_sel_to_face([], _, Sel) -> Sel. 1642 1643vertex_sel_to_vertex([{K,#we{vp=Vtab}=We}|Cs], Vs, Sel) -> 1644 ChartVs = auv2geom_vs(wings_util:array_keys(Vtab), We), 1645 case ordsets:intersection(ChartVs, Vs) of 1646 [] -> 1647 vertex_sel_to_vertex(Cs, Vs, Sel); 1648 VertexSel0 -> 1649 VertexSel1 = geom2auv_vs(VertexSel0, We), 1650 VertexSel = gb_sets:from_list(VertexSel1), 1651 vertex_sel_to_vertex(Cs, Vs, [{K,VertexSel}|Sel]) 1652 end; 1653vertex_sel_to_vertex([], _, Sel) -> Sel. 1654 1655edge_sel_to_edge([{K,#we{es=Etab}=We}|Cs], Es, Sel) -> 1656 Vis = gb_sets:from_ordset(wings_we:visible(We)), 1657 ChartEs0 = [E || {E,#edge{lf=Lf,rf=Rf}} <- array:sparse_to_orddict(Etab), 1658 gb_sets:is_member(Lf, Vis) orelse 1659 gb_sets:is_member(Rf, Vis)], 1660 ChartEs = auv2geom_edges(ChartEs0, We), 1661 case ordsets:intersection(ChartEs, Es) of 1662 [] -> 1663 edge_sel_to_edge(Cs, Es, Sel); 1664 EdgeSel0 -> 1665 EdgeSel1 = geom2auv_edges(EdgeSel0, We), 1666 EdgeSel = gb_sets:from_list(EdgeSel1), 1667 edge_sel_to_edge(Cs, Es, [{K,EdgeSel}|Sel]) 1668 end; 1669edge_sel_to_edge([], _, Sel) -> Sel. 1670 1671%% update_body_sel(SelModeInGeom, Elems, Charts) -> Selection 1672%% Convert the selection from the geoemetry window to 1673%% a body selection in the AutoUV window. 1674 1675update_body_sel(face, Elems, Charts) -> 1676 face_sel_to_body(Charts, Elems, []); 1677update_body_sel(body, _, Charts) -> 1678 body_sel_to_body(Charts, []); 1679update_body_sel(_Mode, _, _) -> 1680 []. 1681 1682face_sel_to_body([{K,We}|Cs], Faces, Sel) -> 1683 Fs = wings_we:visible(We), 1684 case ordsets:intersection(sort(Fs), Faces) of 1685 [] -> 1686 face_sel_to_body(Cs, Faces, Sel); 1687 _ -> 1688 Zero = gb_sets:singleton(0), 1689 face_sel_to_body(Cs, Faces, [{K,Zero}|Sel]) 1690 end; 1691face_sel_to_body([], _, Sel) -> Sel. 1692 1693body_sel_to_body([{K,_}|Cs], Sel) -> 1694 body_sel_to_body(Cs, [{K,gb_sets:singleton(0)}|Sel]); 1695body_sel_to_body([], Sel) -> Sel. 1696 1697%% update_geom_selection(AuvSt) 1698%% Given the selection in the AutoUV window, update the selection 1699%% in the geometry window. 1700 1701update_geom_selection(#st{temp_sel={_,_},bb=#uvstate{st=GeomSt}}) -> 1702 GeomSt; 1703update_geom_selection(#st{sel=[],bb=#uvstate{st=GeomSt}}) -> 1704 wpa:sel_set(face, [], GeomSt); 1705update_geom_selection(#st{selmode=body,sel=Sel, 1706 bb=#uvstate{st=GeomSt,id=Id}}=St) -> 1707 Fs0 = wpa:sel_fold(fun(_, We, A) -> 1708 Fs0 = wings_we:visible(We), 1709 Fs = auv2geom_faces(Fs0, We), 1710 Fs++A 1711 end, [], St#st{sel=Sel}), 1712 Fs = gb_sets:from_list(Fs0), 1713 wpa:sel_set(face, [{Id,Fs}], GeomSt); 1714update_geom_selection(#st{selmode=face,sel=Sel, 1715 bb=#uvstate{st=GeomSt,id=Id}}=St) -> 1716 Fs0 = wpa:sel_fold(fun(Fs, We, A) -> 1717 auv2geom_faces(gb_sets:to_list(Fs), We)++A 1718 end, [], St#st{sel=Sel}), 1719 Fs = gb_sets:from_list(Fs0), 1720 wpa:sel_set(face, [{Id,Fs}], GeomSt); 1721update_geom_selection(#st{selmode=edge,sel=Sel, 1722 bb=#uvstate{st=GeomSt,id=Id}}=St) -> 1723 Es0 = wpa:sel_fold(fun(Es, We, A) -> 1724 auv2geom_edges(gb_sets:to_list(Es), We)++A 1725 end, [], St#st{sel=Sel}), 1726 Es = gb_sets:from_list(Es0), 1727 wpa:sel_set(edge, [{Id,Es}], GeomSt); 1728update_geom_selection(#st{selmode=vertex,sel=Sel, 1729 bb=#uvstate{st=GeomSt,id=Id}}=St) -> 1730 Fs0 = wpa:sel_fold(fun(Vs, We, A) -> 1731 auv2geom_vs(gb_sets:to_list(Vs), We) ++ A 1732 end, [], St#st{sel=Sel}), 1733 Fs = gb_sets:from_list(Fs0), 1734 wpa:sel_set(vertex, [{Id,Fs}], GeomSt). 1735 1736 1737%%%% GUI Operations 1738 1739rotate_chart(Angle, We) -> 1740 Center = wings_vertex:center(We), 1741 rotate_chart(Angle, Center, We). 1742 1743rotate_chart(Angle, Center, We) -> 1744 Rot0 = e3d_mat:translate(e3d_vec:neg(Center)), 1745 Rot1 = e3d_mat:mul(e3d_mat:rotate(float(Angle), {0.0,0.0,1.0}), Rot0), 1746 Rot = e3d_mat:mul(e3d_mat:translate(Center), Rot1), 1747 wings_we:transform_vs(Rot, We). 1748 1749align_chart(Dir, St = #st{selmode=Mode}) -> 1750 wings_sel:map( 1751 fun(Sel, We = #we{vp=Vtab,es=Etab}) -> 1752 case gb_sets:to_list(Sel) of 1753 [V1,V2] when Mode == vertex -> 1754 align_chart(Dir,array:get(V1,Vtab), 1755 array:get(V2,Vtab), 1756 We); 1757 [E] when Mode == edge -> 1758 #edge{vs=V1,ve=V2} = array:get(E, Etab), 1759 align_chart(Dir,array:get(V1,Vtab), 1760 array:get(V2,Vtab), 1761 We); 1762 _ -> align_error(Mode) 1763 end 1764 end, St). 1765 1766align_chart(Dir, V1={X1,Y1,_},V2={X2,Y2,_}, We) -> 1767 Deg0 = 180.0/math:pi() * 1768 case Dir of 1769 align_x -> math:atan2(Y2-Y1,X2-X1); 1770 align_y -> math:atan2(X1-X2,Y2-Y1); 1771 align_xy -> math:atan2(Y2-Y1,X2-X1) - 1772 45/180*math:pi() 1773 end, 1774 Deg = if abs(Deg0) < 90.0 -> Deg0; 1775 true -> Deg0 + 180 1776 end, 1777 Center = e3d_vec:average(V1,V2), 1778 rotate_chart(-Deg,Center,We). 1779 1780-spec align_error(term()) -> no_return(). 1781align_error(Mode) when Mode==edge; Mode==vertex -> 1782 wings_u:error_msg(?__(1,"Select two vertices or one edge")); 1783align_error(body) -> 1784 wings_u:error_msg(?__(2,"Select at least two charts to be aligned to each other")). 1785 1786flip_horizontal(We) -> 1787 flip(e3d_mat:scale(-1.0, 1.0, 1.0), We). 1788 1789flip_vertical(We) -> 1790 flip(e3d_mat:scale(1.0, -1.0, 1.0), We). 1791 1792flip(Flip, We0) -> 1793 Center = wings_vertex:center(We0), 1794 T0 = e3d_mat:translate(e3d_vec:neg(Center)), 1795 T1 = e3d_mat:mul(Flip, T0), 1796 T = e3d_mat:mul(e3d_mat:translate(Center), T1), 1797 We = wings_we:transform_vs(T, We0), 1798 wings_we:invert_normals(We). 1799 1800move_to(Dir,We) -> 1801 [V1={X1,Y1,_},V2={X2,Y2,_}] = wings_vertex:bounding_box(We), 1802 ChartCenter = {CCX,CCY,CCZ} = e3d_vec:average(V1,V2), 1803 Translate 1804 = case Dir of 1805 center -> e3d_vec:sub({0.5,0.5,CCZ}, ChartCenter); 1806 center_x -> e3d_vec:sub({0.5,CCY,CCZ}, ChartCenter); 1807 center_y -> e3d_vec:sub({CCX,0.5,CCZ}, ChartCenter); 1808 bottom -> {0.0,-Y1,0.0}; 1809 top -> {0.0,1.0-Y2,0.0}; 1810 left -> {-X1,0.0,0.0}; 1811 right -> {1.0-X2,0.0,0.0} 1812 end, 1813 T = e3d_mat:translate(Translate), 1814 wings_we:transform_vs(T, We). 1815 1816align(Dir,[{Xa,Ya,_},{Xb,Yb,_}],We) -> 1817 [{X1,Y1,_},{X2,Y2,_}] = wings_vertex:bounding_box(We), 1818 Translate = 1819 case Dir of 1820 bottom -> {0.0,(Ya-Y1),0.0}; 1821 top -> {0.0,(Yb-Y2),0.0}; 1822 left -> {(Xa-X1),0.0,0.0}; 1823 right -> {(Xb-X2),0.0,0.0} 1824 end, 1825 T = e3d_mat:translate(Translate), 1826 wings_we:transform_vs(T, We). 1827 1828stretch(Dir,We) -> 1829 [{X1,Y1,_},{X2,Y2,_}] = wings_vertex:bounding_box(We), 1830 Center = {CX,CY,CZ} = {X1+(X2-X1)/2, Y1+(Y2-Y1)/2, 0.0}, 1831 T0 = e3d_mat:translate(e3d_vec:neg(Center)), 1832 SX0 = 1.0/(X2-X1), SY0= 1.0/(Y2-Y1), 1833 {SX,SY} = case Dir of 1834 max_x -> {SX0, 1.0}; 1835 max_y -> {1.0, SY0}; 1836 max_uniform -> 1837 if SX0 < SY0 -> {SX0, SX0}; 1838 true -> {SY0, SY0} 1839 end; 1840 {max_uniform, Axis} -> 1841 case Axis of 1842 x -> {SX0, SX0}; 1843 y -> {SY0, SY0} 1844 end 1845 end, 1846 Stretch = e3d_mat:scale(SX, SY, 1.0), 1847 T1 = e3d_mat:mul(Stretch, T0), 1848 Pos = case Dir of 1849 {max_uniform,x} -> {CY,CY,CZ}; 1850 {max_uniform,y} -> {CX,CX,CZ}; 1851 max_uniform -> {0.5,0.5,CZ}; 1852 max_x -> {0.5,CY,CZ}; 1853 max_y -> {CX,0.5,CZ} 1854 end, 1855 T = e3d_mat:mul(e3d_mat:translate(Pos), T1), 1856 wings_we:transform_vs(T, We). 1857 1858remap(Method,#st{sel=Sel,selmode=vertex}=St0) -> 1859 %% Check correct pinning. 1860 Ch = fun(Vs, #we{vp=Vtab}, _) -> 1861 case gb_sets:size(Vs) of 1862 N when N /= 2, Method == sphere -> 1863 E = ?__(1,"Select two vertices, the North and South pole"), 1864 wpa:error_msg(E); 1865 N when N < 2 -> 1866 E = ?__(2,"At least two vertices per chart must be pinned"), 1867 wpa:error_msg(E); 1868 N -> 1869 case N < wings_util:array_entries(Vtab) of 1870 true -> ok; 1871 _ -> 1872 E = ?__(5,"All vertices can not be pinned"), 1873 wpa:error_msg(E) 1874 end 1875 end 1876 end, 1877 wings_sel:fold(Ch, ok, St0), 1878 1879 %% OK. Go ahead and re-unfold. 1880 wings_pb:start(?__(3,"remapping")), 1881 wings_pb:update(0.001), 1882 N = length(Sel), 1883 R = fun(Vs, #we{vp=Vtab}=We, I) -> 1884 Msg = ?__(4,"chart")++" " ++ integer_to_list(I+1), 1885 wings_pb:update(I/N, Msg), 1886 Pinned = [begin 1887 {S,T,_} = array:get(V, Vtab), 1888 {V,{S,T}} 1889 end || V <- gb_sets:to_list(Vs)], 1890 {remap(Method, Pinned, Vs, We, St0),I+1} 1891 end, 1892 {St,_} = wings_sel:mapfold(R, 1, St0), 1893 wings_pb:done(update_selected_uvcoords(St)); 1894remap(Method, #st{sel=Sel}=St0) -> 1895 wings_pb:start(?__(3,"remapping")), 1896 wings_pb:update(0.001), 1897 N = length(Sel), 1898 Remap = fun(Elems, We, I) -> 1899 Msg = ?__(4,"chart")++" " ++ integer_to_list(I+1), 1900 wings_pb:update(I/N, Msg), 1901 {remap(Method, none, Elems, We, St0),I+1} 1902 end, 1903 {St,_} = wings_sel:mapfold(Remap, 1, St0), 1904 wings_pb:done(update_selected_uvcoords(St)). 1905 1906remap(proj_lsqcm, Pinned, Sel, We, St) -> 1907 remap({proj,lsqcm}, Pinned, Sel, We, St); 1908remap(proj_slim, Pinned, Sel, We, St) -> 1909 remap({proj,slim}, Pinned, Sel, We, St); 1910 1911remap(stretch_opt, _, _, We, St) -> 1912 Vs3d = orig_pos(We, St), 1913 ?SLOW(auv_mapping:stretch_opt(We, Vs3d)); 1914remap({proj,Method}, _, Sel, We0, St = #st{selmode=face}) -> 1915 Vs3d = orig_pos(We0, St), 1916 try 1917 Fs0 = gb_sets:to_list(Sel), 1918 Vs0 = auv_mapping:projectFromChartNormal(Fs0,We0#we{vp=Vs3d}), 1919 case length(Vs0) == wings_util:array_entries(We0#we.vp) of 1920 true -> %% Everything projected nothing to unfold 1921 update_and_scale_chart(Vs0,We0); 1922 false -> %% Unfold rest of faces 1923 Vtab = array:from_orddict(Vs0), 1924 SelVs = wings_vertex:from_faces(Sel,We0), 1925 Pinned = [begin 1926 {S,T,_} = array:get(V, Vtab), 1927 {V,{S,T}} 1928 end || V <- SelVs], 1929 remap(Method, Pinned, Sel, We0, St) 1930 end 1931 catch throw:{_,What} -> 1932 wpa:error_msg(What); 1933 throw:What -> 1934 wpa:error_msg(What) 1935 end; 1936remap(Type, Pinned, _, We0, St) -> 1937 %% Get 3d positions (even for mapped vs). 1938 Vs3d = orig_pos(We0, St), 1939 case auv_mapping:map_chart(Type, We0#we{vp=Vs3d}, Pinned) of 1940 {error,Msg} -> 1941 wpa:error_msg(Msg); 1942 Vs0 -> 1943 update_and_scale_chart(Vs0,We0) 1944 end. 1945 1946%% gb_tree of every vertex orig 3d position, (including the new cut ones) 1947orig_pos(We = #we{name=#ch{vmap=Vmap}},St) -> 1948 #st{bb=#uvstate{id=Id,st=#st{shapes=Sh}}} = St, 1949 #we{vp=Vs3d0} = gb_trees:get(Id, Sh), 1950 Vs3d = map(fun({V0,_Pos}) -> 1951 case gb_trees:lookup(V0, Vmap) of 1952 none -> 1953 {V0, array:get(V0, Vs3d0)}; 1954 {value,V} -> 1955 {V0, array:get(V, Vs3d0)} 1956 end 1957 end, array:sparse_to_orddict(We#we.vp)), 1958 array:from_orddict(Vs3d). 1959 1960update_and_scale_chart(Vs0,We0) -> 1961 We1 = We0#we{vp=array:from_orddict(sort(Vs0))}, 1962 Fs = wings_we:visible(We1), 1963 OldA = auv_mapping:fs_area(Fs,We0), 1964 NewA = auv_mapping:fs_area(Fs,We1), 1965 NewCenter = wings_vertex:center(We1), 1966 OldCenter = wings_vertex:center(We0), 1967 Scale = math:sqrt(OldA/NewA), 1968 T0 = e3d_mat:translate(e3d_vec:neg(NewCenter)), 1969 Smat = e3d_mat:scale(Scale), 1970 T1 = e3d_mat:mul(Smat, T0), 1971 T = e3d_mat:mul(e3d_mat:translate(OldCenter),T1), 1972 wings_we:transform_vs(T, We1). 1973 1974%%% 1975%%% Draw routines. 1976%%% 1977 1978draw_background(#st{bb=#uvstate{bg_img=Image}}) -> 1979 gl:pushAttrib(?GL_ALL_ATTRIB_BITS), 1980 wings_view:load_matrices(false), 1981 1982 %% Draw border around the UV space. 1983 gl:enable(?GL_DEPTH_TEST), 1984 gl:polygonMode(?GL_FRONT_AND_BACK, ?GL_LINE), 1985 gl:lineWidth(1.0), 1986 gl:color3f(0.0, 0.0, 0.7), 1987 gl:translatef(0.0, 0.0, -0.5), 1988 Bin = << <<V:?F32,20.0:?F32, V:?F32,-20:?F32, 20.0:?F32,V:?F32, -20.0:?F32,V:?F32>> 1989 || V <- lists:seq(-20,20) >>, 1990 wings_vbo:draw(fun(_) -> gl:drawArrays(?GL_LINES, 0, 4*(20+20+1)) end, Bin, [vertex2d]), 1991 gl:lineWidth(3.0), 1992 gl:color3f(0.0, 0.0, 1.0), 1993 gl:recti(0, 0, 1, 1), 1994 1995 %% Draw the background texture. 1996 gl:polygonMode(?GL_FRONT_AND_BACK, ?GL_FILL), 1997 gl:color3f(1.0, 1.0, 1.0), %Clear 1998 case ?GET({?MODULE,show_background}) of 1999 false -> ok; 2000 _ -> 2001 case wings_image:txid(Image) of 2002 none -> ignore; %% Avoid crash if TexImage is deleted 2003 Tx -> 2004 gl:enable(?GL_TEXTURE_2D), 2005 gl:bindTexture(?GL_TEXTURE_2D, Tx) 2006 end 2007 end, 2008 Q = [{0.0, 0.0},{0.0, 0.0, -0.99999}, {1.0, 0.0},{1.0, 0.0, -0.99999}, 2009 {1.0, 1.0},{1.0, 1.0, -0.99999}, {0.0, 1.0},{0.0, 1.0, -0.99999}], 2010 wings_vbo:draw(fun(_) -> gl:drawArrays(?GL_QUADS, 0, 4) end, Q, [uv, vertex]), 2011 2012 gl:disable(?GL_TEXTURE_2D), 2013 2014 gl:popAttrib(). 2015 2016redraw(St) -> 2017 wings_wm:set_prop(show_info_text, false), 2018 wings:redraw(St). 2019 2020init_drawarea() -> 2021 {W0,H0} = wings_wm:top_size(), 2022 W = W0 div 2, 2023 {{W,75},{W,H0-100}}. 2024 2025cleanup_before_exit() -> 2026 %% Ensure the used vbo data will be released. By canceling the Option 2027 %% dialog doesn't remove it automatically. 2028 auv_texture:delete_preview_vbo(), 2029 wings:unregister_postdraw_hook(wings_wm:this(), ?MODULE), 2030 wings_dl:delete_dlists(). 2031 2032%% Generate a checkerboard image of 4x4 squares 2033%% with given side length in pixels. 2034 2035compressed_bg() -> 2036<<131,80,0,0,12,5,120,156,181,211,193,106,19,81,20,6,224,60,128,111,225,131,248, 2037 12,130,224,170,43,209,141,138,96,187,201,74,208,165,136,139,212,214,82,187,105, 2038 153,133,40,184,42,177,138,132,80,170,196,90,104,67,179,154,42,169,138,88,81,132, 2039 136,1,67,146,241,159,254,230,207,157,51,167,205,184,72,249,25,78,134,225,59,247, 2040 158,123,123,189,84,58,83,186,176,116,197,205,167,185,190,155,229,155,127,86,230, 2041 250,249,36,107,103,221,80,123,253,254,221,73,254,239,221,33,243,125,117,144,250, 2042 179,61,104,31,118,135,100,85,140,205,246,122,154,172,159,36,137,235,147,69,240, 2043 1,106,249,248,73,252,213,234,32,227,67,174,95,75,51,106,33,31,91,8,119,65,31, 2044 239,213,133,181,124,224,236,146,241,33,171,75,214,191,247,114,9,81,139,208,231, 2045 100,140,143,149,35,254,124,70,120,126,62,42,66,63,172,195,249,100,10,131,159,48, 2046 31,108,33,244,177,114,115,190,139,55,126,105,62,206,252,57,249,220,124,138,223, 2047 79,247,114,158,114,63,239,60,232,187,169,156,187,236,230,197,252,140,155,100, 2048 230,188,27,106,173,120,40,153,53,181,184,182,173,132,254,215,184,33,153,245,216, 2049 108,108,165,201,250,152,167,240,167,207,7,242,241,190,122,235,33,19,250,120,63, 2050 94,249,113,61,198,43,119,211,140,90,200,135,44,60,244,221,249,208,252,183,242, 2051 208,199,95,182,144,15,89,187,40,232,103,158,147,124,60,209,66,7,49,209,199,226, 2052 247,170,21,60,139,251,97,139,137,62,112,20,120,102,124,78,158,167,16,248,211, 2053 187,159,157,242,186,155,103,107,143,221,204,239,189,117,115,123,225,146,27,106, 2054 189,230,23,197,248,159,219,31,17,227,199,63,127,152,66,96,235,96,27,49,62,102, 2055 216,141,118,152,208,135,220,168,111,33,106,65,13,223,155,66,248,147,141,69,68, 2056 45,228,187,243,193,123,118,97,49,209,135,204,2,239,141,31,14,199,248,220,130, 2057 241,49,25,196,248,74,222,15,135,99,124,110,193,248,213,118,140,20,247,221,243, 2058 229,228,121,10,69,230,163,243,53,254,244,238,103,173,118,213,205,197,104,193,77, 2059 167,94,118,83,142,58,110,168,29,29,237,152,130,218,155,195,3,38,239,247,190,53, 2060 17,227,55,15,123,140,241,49,46,83,80,195,207,251,155,27,136,90,8,239,182,34,68, 2061 45,168,225,251,104,179,139,168,133,88,172,28,201,251,166,160,150,254,191,28,23, 2062 104,97,124,83,200,223,223,127,132,228,125,206,7,91,112,125,51,159,83,252,255, 2063 154,143,252,252,124,92,31,43,231,124,80,20,89,63,39,207,83,48,62,207,23,71,16, 2064 250,211,187,159,127,1,245,246,60,42>>. 2065 2066bg_image() -> 2067 Orig = binary_to_term(compressed_bg()), 2068 Pixels = repeat_image(Orig, []), 2069 Width = Height = 256, 2070 #e3d_image{width=Width,height=Height,image=Pixels, 2071 order=lower_left,name="auvBG"}. 2072 2073repeat_image(<<Row:(32*3)/binary,Rest/binary>>, Acc) -> 2074 repeat_image(Rest,[Row,Row,Row,Row,Row,Row,Row,Row|Acc]); 2075repeat_image(<<>>,Acc) -> 2076 Im = lists:reverse(Acc), 2077 list_to_binary([Im,Im,Im,Im,Im,Im,Im,Im]). 2078 2079%%% 2080%%% Conversion routines. 2081%%% 2082 2083auv2geom_faces(Fs, _) -> 2084 Fs. 2085geom2auv_faces(Fs, _) -> 2086 Fs. 2087 2088auv2geom_vs(Vs, #we{name=#ch{vmap=Vmap}}) -> 2089 sort([auv_segment:map_vertex(V, Vmap) || V <- Vs]). 2090 2091geom2auv_vs(Vs, #we{name=#ch{vmap=Vmap},vp=Vtab}) -> 2092 geom2auv_vs_1(wings_util:array_keys(Vtab), gb_sets:from_list(Vs), Vmap, []). 2093 2094geom2auv_vs_1([V|Vs], VsSet, Vmap, Acc) -> 2095 case gb_sets:is_member(auv_segment:map_vertex(V, Vmap), VsSet) of 2096 true -> geom2auv_vs_1(Vs, VsSet, Vmap, [V|Acc]); 2097 false -> geom2auv_vs_1(Vs, VsSet, Vmap, Acc) 2098 end; 2099geom2auv_vs_1([], _, _, Acc) -> sort(Acc). 2100 2101auv2geom_edges(Es, #we{name=#ch{emap=Emap}}) -> 2102 sort([auv_segment:map_edge(E, Emap) || E <- Es]). 2103 2104geom2auv_edges(Es, #we{name=#ch{emap=Emap0}}) -> 2105 A2We = sofs:relation(gb_trees:to_list(Emap0)), 2106 W2Ae = sofs:relation_to_family(sofs:converse(A2We)), 2107 Tab = gb_trees:from_orddict(sofs:to_external(W2Ae)), 2108 foldl(fun(Edge, Acc) -> 2109 case gb_trees:lookup(Edge, Tab) of 2110 none -> [Edge|Acc]; 2111 {value,Hits} -> Hits ++ Acc 2112 end 2113 end, [], Es). 2114 2115