1%%
2%%  wings_face.erl --
3%%
4%%     This module contains help routines for faces, such as fold functions
5%%     face iterators.
6%%
7
8-module(wings_face).
9
10-export([delete_bad_faces/2, fold/4, fold_faces/4, from_edges/2,
11	 inner_edges/2, to_edges/2, other/2]).
12
13-include("wings.hrl").
14
15from_edges(Es, #we{es=Etab}) when is_list(Es) ->
16    from_edges_1(Es, Etab, []);
17from_edges(Es, We) ->
18    from_edges(gb_sets:to_list(Es), We).
19
20from_edges_1([E|Es], Etab, Acc) ->
21    #edge{lf=Lf,rf=Rf} = gb_trees:get(E, Etab),
22    from_edges_1(Es, Etab, [Lf,Rf|Acc]);
23from_edges_1([], _, Acc) -> gb_sets:from_list(Acc).
24
25%% other(Face, EdgeRecord) -> OtherFace
26%%  Pick up the "other face" from an edge record.
27other(Face, #edge{lf=Face,rf=Other}) -> Other;
28other(Face, #edge{rf=Face,lf=Other}) -> Other.
29
30%% to_edges(Faces, We) -> [Edge]
31%%  Convert a set or list of faces to a list of edges.
32to_edges(Fs, We) ->
33    ordsets:from_list(to_edges_raw(Fs, We)).
34
35%% inner_edges(Faces, We) -> [Edge]
36%%  Given a set of faces, return all inner edges.
37inner_edges(Faces, We) ->
38    S = to_edges_raw(Faces, We),
39    inner_edges_1(lists:sort(S), []).
40
41inner_edges_1([E,E|T], In) ->
42    inner_edges_1(T, [E|In]);
43inner_edges_1([_|T], In) ->
44    inner_edges_1(T, In);
45inner_edges_1([], In) -> lists:reverse(In).
46
47%% Fold over all edges surrounding a face.
48
49fold(F, Acc, Face, #we{es=Etab,fs=Ftab}) ->
50    Edge = gb_trees:get(Face, Ftab),
51    fold(Edge, Etab, F, Acc, Face, Edge, not_done).
52
53fold(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
54fold(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
55    case gb_trees:get(Edge, Etab) of
56	#edge{ve=V,lf=Face,ltsu=NextEdge}=E ->
57	    Acc = F(V, Edge, E, Acc0),
58	    fold(NextEdge, Etab, F, Acc, Face, LastEdge, done);
59	#edge{vs=V,rf=Face,rtsu=NextEdge}=E ->
60	    Acc = F(V, Edge, E, Acc0),
61	    fold(NextEdge, Etab, F, Acc, Face, LastEdge, done)
62    end.
63
64%% Fold over a set of faces.
65
66fold_faces(F, Acc0, [Face|Faces], #we{es=Etab,fs=Ftab}=We) ->
67    Edge = gb_trees:get(Face, Ftab),
68    Acc = fold_faces_1(Edge, Etab, F, Acc0, Face, Edge, not_done),
69    fold_faces(F, Acc, Faces, We);
70fold_faces(_F, Acc, [], _We) -> Acc;
71fold_faces(F, Acc, Faces, We) ->
72    fold_faces(F, Acc, gb_sets:to_list(Faces), We).
73
74fold_faces_1(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
75fold_faces_1(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
76    case gb_trees:get(Edge, Etab) of
77	#edge{ve=V,lf=Face,ltsu=NextEdge}=E ->
78	    Acc = F(Face, V, Edge, E, Acc0),
79	    fold_faces_1(NextEdge, Etab, F, Acc, Face, LastEdge, done);
80	#edge{vs=V,rf=Face,rtsu=NextEdge}=E ->
81	    Acc = F(Face, V, Edge, E, Acc0),
82	    fold_faces_1(NextEdge, Etab, F, Acc, Face, LastEdge, done)
83    end.
84
85%% Return an unsorted list of edges for the faces (with duplicates).
86
87to_edges_raw(Faces, #we{es=Etab,fs=Ftab}) when is_list(Faces) ->
88    to_edges_raw(Faces, Ftab, Etab, []);
89to_edges_raw(Faces, We) ->
90    to_edges_raw(gb_sets:to_list(Faces), We).
91
92to_edges_raw([Face|Faces], Ftab, Etab, Acc0) ->
93    Edge = gb_trees:get(Face, Ftab),
94    Acc = to_edges_raw_1(Edge, Etab, Acc0, Face, Edge, not_done),
95    to_edges_raw(Faces, Ftab, Etab, Acc);
96to_edges_raw([], _, _, Acc) -> Acc.
97
98to_edges_raw_1(LastEdge, _, Acc, _, LastEdge, done) -> Acc;
99to_edges_raw_1(Edge, Etab, Acc, Face, LastEdge, _) ->
100    case gb_trees:get(Edge, Etab) of
101	#edge{lf=Face,ltsu=NextEdge} ->
102	    to_edges_raw_1(NextEdge, Etab, [Edge|Acc], Face, LastEdge, done);
103	#edge{rf=Face,rtsu=NextEdge} ->
104	    to_edges_raw_1(NextEdge, Etab, [Edge|Acc], Face, LastEdge, done)
105    end.
106
107delete_bad_faces(Fs, #we{fs=Ftab,es=Etab}=We) when is_list(Fs) ->
108    Es = bad_edges(Fs, Ftab, Etab, []),
109    wings_edge:dissolve_edges(Es, We);
110delete_bad_faces(Fs, We) ->
111    delete_bad_faces(gb_sets:to_list(Fs), We).
112
113bad_edges([F|Fs], Ftab, Etab, Acc) ->
114    case gb_trees:lookup(F, Ftab) of
115	{value,Edge} ->
116	    case gb_trees:get(Edge, Etab) of
117		#edge{ltpr=Same,ltsu=Same,rtpr=Same,rtsu=Same} ->
118		    erlang:error({internal_error,one_edged_face,F});
119		#edge{ltpr=Same,ltsu=Same} ->
120		    bad_edges(Fs, Ftab, Etab, [Edge|Acc]);
121		#edge{rtpr=Same,rtsu=Same} ->
122		    bad_edges(Fs, Ftab, Etab, [Edge|Acc]);
123		_ -> bad_edges(Fs, Ftab, Etab, Acc)
124	    end;
125	none -> bad_edges(Fs, Ftab, Etab, Acc)
126    end;
127bad_edges([], _, _, Acc) -> Acc.
128