1%%
2%%  wings_sel.erl --
3%%
4%%     This module implements selection utilities.
5%%
6%%  Copyright (c) 2001-2011 Bjorn Gustavsson
7%%
8%%  See the file "license.terms" for information on usage and redistribution
9%%  of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10%%
11%%     $Id$
12%%
13
14-module(wings_sel).
15
16-export([clear/1,reset/1,set/2,set/3,
17	 conditional_reset/1,
18         selected_ids/1,unselected_ids/1,
19	 map/2,map_obj/2,
20         map_update_sel/2,map_update_sel/3,
21	 update_sel/2,update_sel/3,update_sel_all/2,
22         fold_obj/3,fold/3,dfold/4,mapfold/3,
23	 new_sel/3,make/3,valid_sel/1,valid_sel/3,valid_sel_groups/3,
24         clone/2,clone/3,combine/2,combine/3,merge/2,
25	 center/1,center_vs/1,
26	 bbox_center/1,bounding_box/1,
27	 face_regions/2,strict_face_regions/2,edge_regions/2,
28	 select_object/2,deselect_object/2,
29	 get_all_items/2,get_all_items/3,
30	 inverse_items/3,to_vertices/3]).
31
32-export_type([mode/0,
33              edge_set/0,face_set/0,item_id/0,item_set/0,vertex_set/0]).
34
35-include("wings.hrl").
36-include_lib("e3d/e3d.hrl").
37
38-import(lists, [foldl/3,reverse/1,reverse/2,sort/1,keydelete/3,keymember/3]).
39
40-type mode() :: 'vertex' | 'edge' | 'face' | 'body'.
41
42-type vertex_num() :: wings_vertex:vertex_num().
43-type edge_num() :: wings_edge:edge_num().
44-type visible_face_num() :: wings_face:visible_face_num().
45
46-type vertex_set() :: gb_sets:set(vertex_num()).
47-type edge_set() :: gb_sets:set(edge_num()).
48-type face_set() :: gb_sets:set(visible_face_num()).
49
50-type item_id() :: visible_face_num() | edge_num() | vertex_num() | 0.
51-type item_set() :: gb_sets:set(item_id()).
52
53
54-type obj_id() :: non_neg_integer().
55
56-spec clear(#st{}) -> #st{}.
57
58clear(St) ->
59    St#st{sel=[],sh=false}.
60
61-spec reset(#st{}) -> #st{}.
62
63reset(#st{selmode=Mode}=St) ->
64    case Mode of
65	body -> St#st{selmode=face,sel=[],sh=true};
66	_ -> St#st{sel=[],sh=true}
67    end.
68
69-spec conditional_reset(#st{}) -> #st{}.
70
71conditional_reset(#st{sel=[]}=St) ->
72    reset(St);
73conditional_reset(St) ->
74    St#st{sel=[],sh=false}.
75
76-spec set(Sel, #st{}) -> #st{} when
77      Sel :: [{item_id(),item_set()}].
78
79set([], St) ->
80    clear(St);
81set(Sel, St) ->
82    St#st{sel=sort(Sel),sh=false}.
83
84-spec set(Mode, Sel, #st{}) -> #st{} when
85      Mode :: mode(),
86      Sel :: [{item_id(),item_set()}].
87
88set(Mode, [], St) ->
89    clear(St#st{selmode=Mode});
90set(Mode, Sel, St) ->
91    St#st{selmode=Mode,sel=sort(Sel),sh=false}.
92
93
94%%
95%% Return the Ids for all selected objects.
96%%
97
98-spec selected_ids(#st{}) -> [non_neg_integer()].
99
100selected_ids(#st{sel=Sel}) ->
101    [Id || {Id,_} <- Sel].
102
103%%
104%% Return the Ids for all selected objects.
105%%
106
107-spec unselected_ids(#st{}) -> [non_neg_integer()].
108
109unselected_ids(#st{sel=Sel,shapes=Shs}) ->
110    SelIds = [Id || {Id,_} <- Sel],
111    AllIds = gb_trees:keys(Shs),
112    ordsets:subtract(AllIds, SelIds).
113
114%%%
115%%% Map over the selection, modifying the selected #we{}
116%%% records.
117%%%
118
119-spec map(Fun, #st{}) -> #st{} when
120      Fun :: fun((Items, #we{}) -> #we{}),
121      Items :: item_set().
122
123map(F, #st{shapes=Shs0,sel=Sel}=St) ->
124    Shs1 = gb_trees:to_list(Shs0),
125    Shs = map_1(F, Sel, Shs1, St, []),
126    St#st{shapes=Shs}.
127
128
129%%%
130%%% Map over the selection, modifying the object maps.
131%%%
132
133-spec map_obj(F, #st{}) -> #st{} when
134      F :: fun((InObj) -> OutObj),
135      InObj :: wings_obj:obj(),
136      OutObj :: wings_obj:obj().
137
138map_obj(F, #st{sel=Sel}=St) ->
139    SF = fun(#{id:=Id}=Obj) ->
140                 case keymember(Id, 1, Sel) of
141                     false -> Obj;
142                     true -> F(Obj)
143                 end
144         end,
145    wings_obj:map(SF, St).
146
147%%
148%% Map over the selection, modifying the objects and the selection.
149%%
150
151-spec map_update_sel(Fun, Mode, #st{}) -> #st{} when
152      Fun :: fun((InItems, #we{}) -> {#we{},OutItems}),
153      Mode :: mode(),
154      InItems :: item_set(),
155      OutItems :: item_set().
156
157map_update_sel(F, Mode, St0) when is_function(F, 2) ->
158    St = map_update_sel(F, St0),
159    St#st{selmode=Mode}.
160
161-spec map_update_sel(Fun, #st{}) -> #st{} when
162      Fun :: fun((InItems, #we{}) -> {#we{},OutItems}),
163      InItems :: item_set(),
164      OutItems :: item_set().
165
166map_update_sel(F, St0) when is_function(F, 2) ->
167    MF = fun(Sel0, #we{id=Id}=We0, Acc) ->
168		 {We,Sel} = F(Sel0, We0),
169		 case gb_sets:is_empty(Sel) of
170		     true -> {We,Acc};
171		     false -> {We,[{Id,Sel}|Acc]}
172		 end
173	 end,
174    {St,Sel} = mapfold(MF, [], St0),
175    set(Sel, St).
176
177%%
178%% Map over the selection, modifying the selection.
179%%
180
181-spec update_sel(Fun, Mode, #st{}) -> #st{} when
182      Fun :: fun((InItems, #we{}) -> OutItems),
183      Mode :: mode(),
184      InItems :: item_set(),
185      OutItems :: item_set().
186
187update_sel(F, Mode, St0) when is_function(F, 2) ->
188    St = update_sel(F, St0),
189    St#st{selmode=Mode}.
190
191-spec update_sel(Fun, #st{}) -> #st{} when
192      Fun :: fun((Items, #we{}) -> Items),
193      Items :: gb_sets:set(item_id()).
194
195update_sel(F, #st{sel=Sel0,shapes=Shapes}=St) when is_function(F, 2) ->
196    Sel = update_sel_1(Sel0, F, Shapes),
197    set(Sel, St).
198
199%%
200%% Map over all objects, modifying the selection.
201%%
202
203-spec update_sel_all(Fun, #st{}) -> #st{} when
204      Fun :: fun((Items, #we{}) -> Items),
205      Items :: gb_sets:set(item_id()).
206
207update_sel_all(F, #st{sel=Sel0,shapes=Shapes}=St) when is_function(F, 2) ->
208    Sel = update_sel_all_1(gb_trees:values(Shapes), Sel0, F),
209    set(Sel, St).
210
211%%%
212%%% Distributed fold over the selection. The Map function
213%%% will be called in process holding the #we{}. The
214%%% Reduce function will be called in the main Wings
215%%% process.
216%%%
217
218-spec dfold(Map, Reduce, Acc0, #st{}) -> Acc1 when
219      Map :: fun((InItems, #we{}) -> Int),
220      Reduce :: fun((Int, AccIn) -> AccOut),
221      InItems :: item_set(),
222      Acc0 :: term(),
223      Acc1 :: term(),
224      AccIn :: term(),
225      AccOut :: term().
226
227dfold(Map, Reduce, Acc0, St) when is_function(Map, 2),
228				  is_function(Reduce, 2) ->
229    #st{sel=Sel,shapes=Shapes} = St,
230    dfold_1(Sel, Map, Reduce, Shapes, Acc0).
231
232dfold_1([{Id,Items}|T], Map, Reduce, Shapes, Acc0) ->
233    We = gb_trees:get(Id, Shapes),
234    ?ASSERT(We#we.id =:= Id),
235    Int = Map(Items, We),
236    Acc = Reduce(Int, Acc0),
237    dfold_1(T, Map, Reduce, Shapes, Acc);
238dfold_1([], _, _, _, Acc) -> Acc.
239
240%%%
241%%% Fold over the selection of objects (not #we{} records).
242%%%
243
244-spec fold_obj(Fun, Acc0, #st{}) -> Acc1 when
245      Fun :: fun((wings_obj:obj(), AccIn) -> AccOut),
246      Acc0 :: term(),
247      Acc1 :: term(),
248      AccIn :: term(),
249      AccOut :: term().
250
251fold_obj(F, Acc0, #st{sel=Sel}=St) ->
252    FF = fun(#{id:=Id}=Obj, A) ->
253                 case keymember(Id, 1, Sel) of
254                     false -> A;
255                     true -> F(Obj, A)
256                 end
257         end,
258    wings_obj:fold(FF, Acc0, St).
259
260%%%
261%%% Fold over the selection.
262%%%
263
264-spec fold(Fun, Acc0, #st{}) -> Acc1 when
265      Fun :: fun((Items, #we{}, AccIn) -> AccOut),
266      Items :: item_set(),
267      Acc0 :: term(),
268      Acc1 :: term(),
269      AccIn :: term(),
270      AccOut :: term().
271
272fold(F, Acc, #st{sel=Sel,shapes=Shapes}) ->
273    fold_1(F, Acc, Shapes, Sel).
274
275%%%
276%%% Map and fold over the selection.
277%%%
278
279-spec mapfold(Fun, Acc0, #st{}) -> {#st{},Acc1} when
280      Fun :: fun((Items, #we{}, AccIn) -> {#we{},AccOut}),
281      Items :: item_set(),
282      Acc0 :: term(),
283      Acc1 :: term(),
284      AccIn :: term(),
285      AccOut :: term().
286
287mapfold(F, Acc0, #st{shapes=Shs0,sel=Sel}=St) ->
288    Shs1 = gb_trees:to_list(Shs0),
289    {Shs,Acc} = mapfold_1(F, Acc0, Sel, Shs1, St, []),
290    {St#st{shapes=Shs},Acc}.
291
292%%
293%% Construct a new selection based on all visible geometry.
294%%
295%% Light can only be selected in body mode.
296%%
297
298-spec new_sel(Fun, Mode, #st{}) -> #st{} when
299      Fun :: fun((InItems, #we{}) -> OutItems),
300      Mode :: mode(),
301      InItems :: item_set(),
302      OutItems :: item_set().
303
304new_sel(F, Mode, #st{shapes=Shapes}=St) when is_function(F, 2) ->
305    Sel0 = gb_trees:values(Shapes),
306    Sel = new_sel_1(Sel0, F, Mode),
307    St#st{selmode=Mode,sel=Sel}.
308
309%% make(Filter, Mode, St) -> [{Id,ItemGbSet}].
310%%      Mode = body|face|edge|vertex
311%%      Filter(Item, We) -> true|false
312%%      Item = face() | edge() | vertex() | 0
313%%  Construct a selection by calling Filter(Item, We) for each
314%%  item in each object (where Item is either a face number,
315%%  edge number, vertex number, or 0 depending on Mode).
316%%
317
318-spec make(Fun, Mode, #st{}) -> #st{} when
319      Fun :: fun((Items, #we{}) -> boolean()),
320      Mode :: mode(),
321      Items :: item_set().
322
323make(Filter, Mode, St) when is_function(Filter, 2) ->
324    new_sel(fun(Sel, We) ->
325		    gb_sets:filter(fun(Item) -> Filter(Item, We) end, Sel)
326	    end, Mode, St).
327
328
329%%
330%% Clone the selection.
331%%
332
333-type suffix() :: 'cut' | 'clone' | 'copy' | 'extract' | 'mirror' | 'sep'.
334-type clone_item() :: {#we{},item_set(),suffix()}.
335-type clone_out() :: {#we{},item_set(),[clone_item()]}.
336-type clone_fun() :: fun((item_set(), #we{}) -> clone_out()).
337
338-spec clone(Fun, #st{}) -> #st{} when
339      Fun :: clone_fun().
340
341clone(F, St0) ->
342    MF = fun(Items, We, Acc) ->
343                 clone_fun(F, Items, We, Acc)
344         end,
345    #st{sel=Sel} = St = fold(MF, St0#st{sel=[]}, St0),
346    St#st{sel=sort(Sel)}.
347
348-spec clone(Fun, Mode, #st{}) -> #st{} when
349      Fun :: clone_fun(),
350      Mode :: mode().
351
352clone(Fun, Mode, St0) ->
353    St = clone(Fun, St0),
354    St#st{selmode=Mode}.
355
356%%
357%% Combine selected objects to one.
358%%
359
360-spec combine(Fun, #st{}) -> #st{} when
361      Fun :: fun((item_set(), #we{}) -> {#we{},item_set()}).
362
363combine(F, St) ->
364    MF = fun(Wes, Sel, Mode) ->
365                 Zipped = combine_zip(Wes, Sel, Mode),
366                 {We,Items0} = wings_we:merge_root_set(Zipped),
367                 Items1 = combine_items(Mode, Items0),
368                 Items = gb_sets:from_ordset(Items1),
369                 F(Items, We)
370         end,
371    comb_merge(MF, St).
372
373-spec combine(Fun, Mode, #st{}) -> #st{} when
374      Fun :: fun((item_set(), #we{}) -> {#we{},item_set()}),
375      Mode :: mode().
376
377combine(F, Mode, St0) ->
378    St = combine(F, St0),
379    St#st{selmode=Mode}.
380
381-spec merge(Fun, #st{}) -> #st{} when
382      Fun :: fun(({#we{},item_set()}) -> {#we{},item_set()}).
383
384merge(F, St) when is_function(F, 1) ->
385    MF = fun(Wes, Sel, _Mode) ->
386                 Zipped = merge_zip(Wes, Sel),
387                 F(Zipped)
388         end,
389    comb_merge(MF, St).
390
391%%%
392%%% Calculate the center for all selected objects.
393%%%
394
395-spec center(#st{}) -> e3d_vec:vector().
396
397center(#st{selmode=Mode}=St) ->
398    MF = fun(Items, We) ->
399		 Vs = to_vertices(Mode, Items, We),
400		 wings_vertex:center(Vs, We)
401	 end,
402    RF = fun(V, {N,Acc}) -> {N+1,e3d_vec:add(V, Acc)} end,
403    {N,Sum} = dfold(MF, RF, {0,e3d_vec:zero()}, St),
404    e3d_vec:divide(Sum, N).
405
406
407%%%
408%%% Calculate the center for all selected vertices.
409%%%
410
411-spec center_vs(#st{}) -> e3d_vec:vector().
412
413center_vs(#st{selmode=Mode}=St) ->
414    MF = fun(Items, We) ->
415		 Vs = to_vertices(Mode, Items, We),
416                 N = length(Vs),
417		 Center = wings_vertex:center(Vs, We),
418		 {N,e3d_vec:mul(Center, float(N))}
419	 end,
420    RF = fun({N0,C0}, {N1,C1}) -> {N0+N1,e3d_vec:add(C0, C1)} end,
421    {N,Sum} = dfold(MF, RF, {0,e3d_vec:zero()}, St),
422    e3d_vec:divide(Sum, N).
423
424%% Calculate center of bounding box.
425
426-spec bbox_center(#st{}) -> e3d_vec:vector().
427
428bbox_center(St) ->
429    BBox = bounding_box(St),
430    e3d_vec:average(BBox).
431
432%%%
433%%% Calculate the bounding-box for the selection.
434%%%
435
436-spec bounding_box(#st{}) -> [e3d_vec:vector()] | 'none'.
437
438bounding_box(#st{selmode=Mode}=St) ->
439    MF = fun(Items, We) ->
440		 Vs = to_vertices(Mode, Items, We),
441		 wings_vertex:bounding_box(Vs, We)
442	 end,
443    RF = fun(Box, none) -> Box;
444	    (Box, Acc) -> e3d_vec:bounding_box(Box++Acc)
445	 end,
446    dfold(MF, RF, none, St).
447
448%%%
449%%% Divide the face selection into regions where each face shares at least
450%%% one edge with another face in the same region. Two faces can share a
451%%% vertex without necessarily being in the same region.
452%%%
453
454-spec face_regions(Faces, #we{}) -> [face_set()] when
455      Faces :: face_set() | [visible_face_num()].
456
457face_regions(Faces, We) when is_list(Faces) ->
458    face_regions_1(gb_sets:from_list(Faces), We);
459face_regions(Faces, We) ->
460    face_regions_1(Faces, We).
461
462%%%
463%%% Divide the face selection into regions where each face shares at least
464%%% one vertex with another face in the same region.
465%%%
466
467-spec strict_face_regions(Faces, #we{}) -> [face_set()] when
468      Faces :: face_set() | [visible_face_num()].
469
470strict_face_regions(Faces, We) when is_list(Faces) ->
471    find_strict_face_regions(gb_sets:from_list(Faces), We, []);
472strict_face_regions(Faces, We) ->
473    find_strict_face_regions(Faces, We, []).
474
475%%%
476%%% Here we want to divide the selection into regions of connected edges.
477%%% We use a standard working-set algorithm.
478%%%
479
480-spec edge_regions(Edges, #we{}) -> [edge_set()] when
481      Edges :: edge_set() | [edge_num()].
482
483edge_regions(Edges, We) when is_list(Edges) ->
484    find_edge_regions(gb_sets:from_list(Edges), We, []);
485edge_regions(Edges, We) ->
486    find_edge_regions(Edges, We, []).
487
488%%%
489%%% Validate selection.
490%%%
491
492-spec valid_sel(#st{}) -> #st{}.
493
494valid_sel(#st{sel=Sel,selmode=Mode}=St) ->
495    St#st{sel=valid_sel(Sel, Mode, St)}.
496
497-spec valid_sel(SelIn, Mode, #st{}) -> SelOut when
498      Mode :: mode(),
499      SelIn :: [{item_id(),item_set()}],
500      SelOut :: [{item_id(),item_set()}].
501
502valid_sel(Sel0, Mode, #st{shapes=Shapes}) ->
503    Sel = foldl(
504	    fun({Id,Items0}, A) ->
505		    case gb_trees:lookup(Id, Shapes) of
506			none -> A;
507			{value,#we{perm=Perm}} when ?IS_NOT_SELECTABLE(Perm) ->
508			    A;
509			{value,We} ->
510			    Items = validate_items(Items0, Mode, We),
511			    case gb_sets:is_empty(Items) of
512				false -> [{Id,Items}|A];
513				true -> A
514			    end
515		    end
516	    end, [], Sel0),
517    reverse(Sel).
518
519-spec valid_sel_groups(SelIn, Mode, #st{}) -> SelOut when
520    Mode :: mode(),
521    SelIn :: [{item_id(),item_set()}],
522    SelOut :: [{item_id(),item_set()}].
523
524valid_sel_groups(Sel0, Mode, #st{shapes=Shapes}) ->	% allow wings to save any selection groups
525    Sel = foldl(
526	fun({Id,Items0}, A) ->
527	    case gb_trees:lookup(Id, Shapes) of
528		none -> A;
529		{value,We} ->
530		    Items = validate_items(Items0, Mode, We),
531		    case gb_sets:is_empty(Items) of
532			false -> [{Id,Items}|A];
533			true -> A
534		    end
535	    end
536	end, [], Sel0),
537    reverse(Sel).
538
539-spec select_object(obj_id(), #st{}) -> #st{}.
540
541select_object(Id, #st{selmode=Mode,sel=Sel0}=St) ->
542    case keymember(Id, 1, Sel0) of
543	true -> St;
544	false ->
545	    Sel = sort([{Id,get_all_items(Mode, Id, St)}|Sel0]),
546	    St#st{sel=Sel}
547    end.
548
549-spec deselect_object(obj_id(), #st{}) -> #st{}.
550
551deselect_object(Id, #st{sel=Sel0}=St) ->
552    Sel = keydelete(Id, 1, Sel0),
553    St#st{sel=Sel}.
554
555-spec inverse_items(Mode, InItems, #we{}) -> OutItems when
556      Mode :: mode(),
557      InItems :: item_set(),
558      OutItems :: item_set().
559
560inverse_items(Mode, Elems, We) ->
561    gb_sets:difference(get_all_items(Mode, We), Elems).
562
563-spec get_all_items(mode(), obj_id(), #st{}) -> item_set().
564
565get_all_items(Mode, Id, #st{shapes=Shapes}) ->
566    We = gb_trees:get(Id, Shapes),
567    get_all_items(Mode, We).
568
569-spec to_vertices(Mode, Items, #we{}) -> Vertices when
570      Mode :: mode(),
571      Items :: item_set(),
572      Vertices :: [vertex_num()].
573
574to_vertices(vertex, Vs, _) ->
575    gb_sets:to_list(Vs);
576to_vertices(face, Faces, We) ->
577    wings_face:to_vertices(Faces, We);
578to_vertices(edge, Edges, We) ->
579    wings_edge:to_vertices(Edges, We);
580to_vertices(body, _, #we{vp=Vtab}) ->
581    wings_util:array_keys(Vtab).
582
583%%%
584%%% Local functions.
585%%%
586
587map_1(F, [{Id,Items}|Sel], [{Id,We0}|Shs], St, Acc) ->
588    ?ASSERT(We0#we.id =:= Id),
589    #we{es=Etab} = We = F(Items, We0),
590    case wings_util:array_is_empty(Etab) of
591	true -> map_1(F, Sel, Shs, St, Acc);
592	false -> map_1(F, Sel, Shs, St, [{Id,We}|Acc])
593    end;
594map_1(F, [_|_]=Sel, [Pair|Shs], St, Acc) ->
595    map_1(F, Sel, Shs, St, [Pair|Acc]);
596map_1(_F, [], Shs, _St, Acc) ->
597    gb_trees:from_orddict(reverse(Acc, Shs)).
598
599update_sel_1([{Id,Sel0}|T], F, Shapes) ->
600    We = gb_trees:get(Id, Shapes),
601    ?ASSERT(We#we.id =:= Id),
602    Sel = F(Sel0, We),
603    case gb_sets:is_empty(Sel) of
604	false ->
605	    [{Id,Sel}|update_sel_1(T, F, Shapes)];
606	true ->
607	    update_sel_1(T, F, Shapes)
608    end;
609update_sel_1([], _, _) -> [].
610
611update_sel_all_1([#we{id=Id}=We|Wes], [{Id,Items0}|Sel], F) ->
612    Items = F(Items0, We),
613    case gb_sets:is_empty(Items) of
614	false ->
615	    [{Id,Items}|update_sel_all_1(Wes, Sel, F)];
616	true ->
617            update_sel_all_1(Wes, Sel, F)
618    end;
619update_sel_all_1([#we{id=Id,perm=P}|Wes]=Wes0, Sel, F) ->
620    if
621        ?IS_SELECTABLE(P) ->
622            update_sel_all_1(Wes0, [{Id,gb_sets:empty()}|Sel], F);
623        true ->
624            update_sel_all_1(Wes, Sel, F)
625    end;
626update_sel_all_1([], _, _) -> [].
627
628fold_1(F, Acc0, Shapes, [{Id,Items}|T]) ->
629    We = gb_trees:get(Id, Shapes),
630    ?ASSERT(We#we.id =:= Id),
631    fold_1(F, F(Items, We, Acc0), Shapes, T);
632fold_1(_F, Acc, _Shapes, []) -> Acc.
633
634mapfold_1(F, Acc0, [{Id,Items}|Sel], [{Id,We0}|Shs], St, ShsAcc) ->
635    ?ASSERT(We0#we.id =:= Id),
636    {#we{es=Etab}=We,Acc} = F(Items, We0, Acc0),
637    case wings_util:array_is_empty(Etab) of
638	true -> mapfold_1(F, Acc0, Sel, Shs, St, ShsAcc);
639	false -> mapfold_1(F, Acc, Sel, Shs, St, [{Id,We}|ShsAcc])
640    end;
641mapfold_1(F, Acc, [_|_]=Sel, [Pair|Shs], St, ShsAcc) ->
642    mapfold_1(F, Acc, Sel, Shs, St, [Pair|ShsAcc]);
643mapfold_1(_F, Acc, [], Shs, _St, ShsAcc) ->
644    {gb_trees:from_orddict(reverse(ShsAcc, Shs)),Acc}.
645
646new_sel_1([#we{perm=Perm}|Shs], F, Mode) when ?IS_NOT_SELECTABLE(Perm) ->
647    new_sel_1(Shs, F, Mode);
648new_sel_1([We|Shs], F, Mode) when ?IS_LIGHT(We), Mode =/= body ->
649    new_sel_1(Shs, F, Mode);
650new_sel_1([#we{id=Id}=We|Shs], F, Mode) ->
651    Sel0 = get_all_items(Mode, We),
652    Sel = F(Sel0, We),
653    case gb_sets:is_empty(Sel) of
654	false ->
655	    [{Id,Sel}|new_sel_1(Shs, F, Mode)];
656	true ->
657	    new_sel_1(Shs, F, Mode)
658    end;
659new_sel_1([], _, _) -> [].
660
661clone_fun(F, Items0, #we{id=Id}=We0, #st{shapes=Shs0}=St0) ->
662    {We,Items,New} = F(Items0, We0),
663    Shs = gb_trees:update(Id, We, Shs0),
664    St1 = St0#st{shapes=Shs},
665    St = clone_add_sel(Items, Id, St1),
666    clone_fun_add(New, St).
667
668clone_fun_add([{#we{name=Name0}=We0,Items,Suffix}|T],
669              #st{onext=Id,shapes=Shs0}=St0) ->
670    Name = new_name(Name0, Suffix, Id),
671    We = We0#we{id=Id,name=Name},
672    Shs = gb_trees:insert(Id, We, Shs0),
673    St1 = St0#st{shapes=Shs,onext=Id+1},
674    St = clone_add_sel(Items, Id, St1),
675    clone_fun_add(T, St);
676clone_fun_add([], St) -> St.
677
678clone_add_sel(Items, Id, #st{sel=Sel}=St) ->
679    case gb_sets:is_empty(Items) of
680        false -> St#st{sel=[{Id,Items}|Sel]};
681        true -> St
682    end.
683
684new_name(OldName, Suffix0, Id) ->
685    Suffix = suffix(Suffix0),
686    Base = base(reverse(OldName)),
687    reverse(Base, "_" ++ Suffix ++ integer_to_list(Id)).
688
689%% Note: Filename suffixes are intentionally not translated.
690%% If we are to translate them in the future, base/1 below
691%% must be updated to strip suffixes (both for the current language
692%% and for English).
693
694suffix(cut) -> "cut";
695suffix(clone) -> "clone";
696suffix(copy) -> "copy";
697suffix(extract) -> "extract";
698suffix(mirror) -> "mirror";
699suffix(sep) -> "sep".
700
701%% base_1(ReversedName) -> ReversedBaseName
702%%  Given an object name, strip digits and known suffixes to
703%%  create a base name. Returns the unchanged name if
704%%  no known suffix could be stripped.
705
706base(OldName) ->
707    case base_1(OldName) of
708	error -> OldName;
709	Base -> Base
710    end.
711
712base_1([H|T]) when $0 =< H, H =< $9 -> base_1(T);
713base_1("tuc_"++Base) -> Base;			%"_cut"
714base_1("enolc_"++Base) -> Base;			%"_clone"
715base_1("ypoc_"++Base) -> Base;			%"_copy"
716base_1("tcartxe_"++Base) -> Base;		%"_extract"
717base_1("rorrim_"++Base) -> Base;		%"_mirror"
718base_1("pes_"++Base) -> Base;			%"_sep"
719base_1(_Base) -> error.
720
721comb_merge(MF, #st{shapes=Shs0,selmode=Mode,sel=[{Id,_}|_]=Sel0}=St) ->
722    Shs1 = sofs:from_external(gb_trees:to_list(Shs0), [{id,object}]),
723    Sel1 = sofs:from_external(Sel0, [{id,dummy}]),
724    Sel2 = sofs:domain(Sel1),
725    {Wes0,Shs2} = sofs:partition(1, Shs1, Sel2),
726    Wes = sofs:to_external(sofs:range(Wes0)),
727    {We0,Items} = MF(Wes, Sel0, Mode),
728    We = case lists:usort([wings_obj:get_folder(We) || We <- Wes]) of
729             [Folder] -> wings_obj:set_folder(Folder, We0);
730             _ -> We0 %% From different folders add to root folder
731         end,
732    Shs = gb_trees:from_orddict(sort([{Id,We}|sofs:to_external(Shs2)])),
733    Sel = case gb_sets:is_empty(Items) of
734              true -> [];
735              false -> [{Id,Items}]
736          end,
737    St#st{shapes=Shs,sel=Sel}.
738
739combine_zip([#we{id=Id}=We|Wes], [{Id,Items0}|Sel], Mode) ->
740    RootSet = combine_root_set(Mode, Items0),
741    [{We,RootSet}|combine_zip(Wes, Sel, Mode)];
742combine_zip([], [], _) -> [].
743
744combine_root_set(body, _Items) ->
745    [];
746combine_root_set(Mode, Items) ->
747    [{Mode,Item} || Item <- gb_sets:to_list(Items)].
748
749combine_items(body, _RootSet) ->
750    [0];
751combine_items(_, RootSet) ->
752    ordsets:from_list([Item || {_,Item} <- RootSet]).
753
754merge_zip([#we{id=Id}=We|Wes], [{Id,Items}|Sel]) ->
755    [{We,Items}|merge_zip(Wes, Sel)];
756merge_zip([], []) -> [].
757
758
759face_regions_1(Faces, We) ->
760    find_face_regions(Faces, We, fun collect_face_fun/5, []).
761
762find_face_regions(Faces0, We, Coll, Acc) ->
763    case gb_sets:is_empty(Faces0) of
764	true -> Acc;
765	false ->
766	    {Face,Faces1} = gb_sets:take_smallest(Faces0),
767	    Ws = [Face],
768	    {Reg,Faces} = collect_face_region(Ws, We, Coll, [], Faces1),
769	    find_face_regions(Faces, We, Coll, [Reg|Acc])
770    end.
771
772collect_face_region([_|_]=Ws0, We, Coll, Reg0, Faces0) ->
773    Reg = Ws0++Reg0,
774    {Ws,Faces} = wings_face:fold_faces(Coll, {[],Faces0}, Ws0, We),
775    collect_face_region(Ws, We, Coll, Reg, Faces);
776collect_face_region([], _, _, Reg, Faces) ->
777    {gb_sets:from_list(Reg),Faces}.
778
779collect_face_fun(Face, _, _, Rec, {Ws,Faces}=A) ->
780    Of = case Rec of
781	     #edge{lf=Face,rf=Of0} -> Of0;
782	     #edge{rf=Face,lf=Of0} -> Of0
783	 end,
784    case gb_sets:is_member(Of, Faces) of
785	true -> {[Of|Ws],gb_sets:delete(Of, Faces)};
786	false -> A
787    end.
788
789find_strict_face_regions(Faces0, We, Acc) ->
790    case gb_sets:is_empty(Faces0) of
791	true -> Acc;
792	false ->
793	    {Face,Faces1} = gb_sets:take_smallest(Faces0),
794	    Ws = gb_sets:singleton(Face),
795	    {Reg,Faces} = collect_strict_face_region(Ws, We, [], Faces1),
796	    find_strict_face_regions(Faces, We, [Reg|Acc])
797    end.
798
799collect_strict_face_region(Ws0, We, Reg0, Faces0) ->
800    case gb_sets:is_empty(Ws0) of
801	true -> {gb_sets:from_list(Reg0),Faces0};
802	false ->
803	    {Face,Ws1} = gb_sets:take_smallest(Ws0),
804	    Reg = [Face|Reg0],
805	    {Ws,Faces} = collect_strict_adj_sel(Face, We, Ws1, Faces0),
806	    collect_strict_face_region(Ws, We, Reg, Faces)
807    end.
808
809collect_strict_adj_sel(Face, We, Ws0, Faces0) ->
810    wings_face:fold(
811      fun(V, _, _, A0) ->
812	      wings_vertex:fold(
813		fun(_, Of, _, {W0,F0}=A1) ->
814		   case gb_sets:is_member(Of, F0) of
815		       true -> {gb_sets:insert(Of, W0),gb_sets:delete(Of, F0)};
816		       false -> A1
817		   end
818		end, A0, V, We)
819      end, {Ws0,Faces0}, Face, We).
820
821find_edge_regions(Edges0, We, Acc) ->
822    case gb_sets:is_empty(Edges0) of
823	true -> Acc;
824	false ->
825	    {Edge,Edges1} = gb_sets:take_smallest(Edges0),
826	    Ws = gb_sets:singleton(Edge),
827	    {Reg,Edges} = find_all_adj_edges(Ws, We, [], Edges1),
828	    find_edge_regions(Edges, We, [Reg|Acc])
829    end.
830
831find_all_adj_edges(Ws0, #we{es=Etab}=We, Reg0, Edges0) ->
832    case gb_sets:is_empty(Ws0) of
833	true -> {gb_sets:from_list(Reg0),Edges0};
834	false ->
835	    {Edge,Ws1} = gb_sets:take_smallest(Ws0),
836	    Reg = [Edge|Reg0],
837	    #edge{vs=Va,ve=Vb} = array:get(Edge, Etab),
838	    Adj0 = add_adjacent_edges(Va, We, []),
839	    Adj1 = add_adjacent_edges(Vb, We, Adj0),
840	    Adj = gb_sets:from_list(Adj1),
841	    AdjSel = gb_sets:intersection(Adj, Edges0),
842	    Ws = gb_sets:union(Ws1, AdjSel),
843	    Edges = gb_sets:difference(Edges0, AdjSel),
844	    find_all_adj_edges(Ws, We, Reg, Edges)
845    end.
846
847add_adjacent_edges(V, We, Acc) ->
848    wings_vertex:fold(fun(Edge, _, _, A) -> [Edge|A] end, Acc, V, We).
849
850validate_items(Items, body, _We) -> Items;
851validate_items(Items, Mode, We) ->
852    gb_sets:intersection(Items, get_all_items(Mode, We)).
853
854get_all_items(vertex, We) ->
855    gb_sets:from_ordset(wings_we:visible_vs(We));
856get_all_items(edge, We) ->
857    gb_sets:from_ordset(wings_we:visible_edges(We));
858get_all_items(face, We) ->
859    gb_sets:from_ordset(wings_we:visible(We));
860get_all_items(body, _) ->
861    gb_sets:singleton(0).
862