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