4
%% This module contains functions to build and manipulate
5
%% we records (winged-edged records, the central data structure
10
-export([rebuild/1, is_consistent/1, is_face_consistent/2, new_id/1,
11
new_items_as_ordset/3, validate_mirror/1, visible/1, visible_edges/1]).
13
-include("wings.hrl").
19
validate_mirror(#we{mirror=none}=We) -> We;
20
validate_mirror(#we{fs=Ftab,mirror=Face}=We) ->
21
case gb_trees:is_defined(Face, Ftab) of
22
false -> We#we{mirror=none};
27
%% Rebuild any missing 'vc' and 'fs' tables. If there are
28
%% fewer elements in the 'vc' table than in the 'vp' table,
29
%% remove redundant entries in the 'vp' table. Updated id
31
rebuild(#we{vc=undefined,fs=undefined,es=Etab0}=We0) ->
32
Etab = gb_trees:to_list(Etab0),
33
Ftab = rebuild_ftab(Etab),
34
VctList = rebuild_vct(Etab),
35
We = We0#we{vc=gb_trees:from_orddict(VctList),fs=Ftab},
36
rebuild_1(VctList, We);
37
rebuild(#we{vc=undefined,es=Etab}=We) ->
38
VctList = rebuild_vct(gb_trees:to_list(Etab), []),
39
rebuild_1(VctList, We#we{vc=gb_trees:from_orddict(VctList)});
40
rebuild(#we{fs=undefined,es=Etab}=We) ->
41
Ftab = rebuild_ftab(gb_trees:to_list(Etab)),
42
rebuild(We#we{fs=Ftab});
43
rebuild(We) -> update_id_bounds(We).
45
%%% Utilities for allocating IDs.
47
new_id(#we{next_id=Id}=We) ->
48
{Id,We#we{next_id=Id+1}}.
50
%%% Returns sets of newly created items.
52
new_items_as_ordset(vertex, #we{next_id=Wid}, #we{next_id=NewWid,vp=Tab}) ->
53
new_items_as_ordset_1(Tab, Wid, NewWid);
54
new_items_as_ordset(edge, #we{next_id=Wid}, #we{next_id=NewWid,es=Tab}) ->
55
new_items_as_ordset_1(Tab, Wid, NewWid);
56
new_items_as_ordset(face, #we{next_id=Wid}, #we{next_id=NewWid,fs=Tab}) ->
57
new_items_as_ordset_1(Tab, Wid, NewWid).
59
any_hidden(#we{fs=Ftab}) ->
60
not gb_trees:is_empty(Ftab) andalso
61
wings_util:gb_trees_smallest_key(Ftab) < 0.
67
rebuild_1(VctList, #we{vc=Vct,vp=Vtab0}=We) ->
68
case {gb_trees:size(Vct),gb_trees:size(Vtab0)} of
69
{Same,Same} -> rebuild(We);
70
{Sz1,Sz2} when Sz1 < Sz2 ->
71
Vtab = vertex_gc_1(VctList, gb_trees:to_list(Vtab0), []),
72
rebuild(We#we{vp=Vtab})
78
rebuild_vct([{Edge,#edge{vs=Va,ve=Vb}}|Es], Acc0) ->
79
Acc = rebuild_maybe_add(Va, Vb, Edge, Acc0),
81
rebuild_vct([], VtoE) ->
82
build_incident_tab(VtoE).
85
rebuild_ftab_1(Es, []).
87
rebuild_ftab_1([{Edge,#edge{lf=Lf,rf=Rf}}|Es], Acc0) ->
88
Acc = rebuild_maybe_add(Lf, Rf, Edge, Acc0),
89
rebuild_ftab_1(Es, Acc);
90
rebuild_ftab_1([], FtoE) ->
91
gb_trees:from_orddict(build_incident_tab(FtoE)).
93
rebuild_maybe_add(Ka, Kb, E, [_,{Ka,_}|_]=Acc) ->
95
rebuild_maybe_add(Ka, Kb, E, [_,{Kb,_}|_]=Acc) ->
97
rebuild_maybe_add(Ka, Kb, E, [{Ka,_}|_]=Acc) ->
99
rebuild_maybe_add(Ka, Kb, E, [{Kb,_}|_]=Acc) ->
101
rebuild_maybe_add(Ka, Kb, E, Acc) ->
104
vertex_gc_1([{V,_}|Vct], [{V,_}=Vtx|Vpos], Acc) ->
105
vertex_gc_1(Vct, Vpos, [Vtx|Acc]);
106
vertex_gc_1([_|_]=Vct, [_|Vpos], Acc) ->
107
vertex_gc_1(Vct, Vpos, Acc);
108
vertex_gc_1([], _, Acc) ->
109
gb_trees:from_orddict(lists:reverse(Acc)).
112
%%% Handling of hidden faces.
115
visible(#we{mirror=none,fs=Ftab}) ->
116
visible_2(gb_trees:keys(Ftab));
117
visible(#we{mirror=Face,fs=Ftab}) ->
118
visible_2(gb_trees:keys(gb_trees:delete(Face, Ftab))).
120
visible_2([F|Fs]) when F < 0 -> visible_2(Fs);
123
visible_edges(#we{es=Etab,mirror=Face}=We) ->
124
case any_hidden(We) of
125
false -> gb_trees:keys(Etab);
126
true -> visible_es_1(gb_trees:to_list(Etab), Face, [])
129
visible_es_1([{E,#edge{lf=Lf,rf=Rf}}|Es], Face, Acc) ->
134
Rf < 0; Rf =:= Face ->
135
%% Both faces invisible (in some way).
136
visible_es_1(Es, Face, Acc);
138
%% Right face is visible.
139
visible_es_1(Es, Face, [E|Acc])
141
Lf =:= Face, Rf < 0 ->
142
%% Left face mirror, right face hidden.
143
visible_es_1(Es, Face, Acc);
145
%% At least one face visible.
146
visible_es_1(Es, Face, [E|Acc])
148
visible_es_1([], _, Acc) -> ordsets:from_list(Acc).
150
update_id_bounds(#we{vp=Vtab,es=Etab,fs=Ftab}=We) ->
151
case gb_trees:is_empty(Etab) of
152
true -> We#we{next_id=0};
154
LastId = lists:max([wings_util:gb_trees_largest_key(Vtab),
155
wings_util:gb_trees_largest_key(Etab),
156
wings_util:gb_trees_largest_key(Ftab)]),
157
We#we{next_id=LastId+1}
160
%% build_incident_tab([{Elem,Edge}]) -> [{Elem,Edge}]
161
%% Elem = Face or Vertex
162
%% Build the table of incident edges for either faces or vertices.
163
%% Returns an ordered list where each Elem is unique.
165
build_incident_tab(ElemToEdgeRel) ->
166
T = ets:new(?MODULE, [ordered_set]),
167
ets:insert(T, ElemToEdgeRel),
173
%%% Calculate normals.
176
new_items_as_ordset_1(Tab, Wid, NewWid) when NewWid-Wid < 32 ->
177
new_items_as_ordset_2(Wid, NewWid, Tab, []);
178
new_items_as_ordset_1(Tab, Wid, _NewWid) ->
179
[Item || Item <- gb_trees:keys(Tab), Item >= Wid].
181
new_items_as_ordset_2(Wid, NewWid, Tab, Acc) when Wid < NewWid ->
182
case gb_trees:is_defined(Wid, Tab) of
183
true -> new_items_as_ordset_2(Wid+1, NewWid, Tab, [Wid|Acc]);
184
false -> new_items_as_ordset_2(Wid+1, NewWid, Tab, Acc)
186
new_items_as_ordset_2(_Wid, _NewWid, _Tab, Acc) -> lists:reverse(Acc).
189
%%% Test the consistency of a #we{}.
192
is_consistent(#we{}=We) ->
194
validate_vertex_tab(We),
196
catch error:_ -> false
199
is_face_consistent(Face, #we{fs=Ftab,es=Etab}) ->
200
Edge = gb_trees:get(Face, Ftab),
201
try validate_face(Face, Edge, Etab)
202
catch error:_ -> false
205
validate_faces(#we{fs=Ftab,es=Etab}) ->
206
validate_faces_1(gb_trees:to_list(Ftab), Etab).
208
validate_faces_1([{Face,Edge}|Fs], Etab) ->
209
validate_face(Face, Edge, Etab),
210
validate_faces_1(Fs, Etab);
211
validate_faces_1([], _) -> true.
213
validate_face(Face, Edge, Etab) ->
214
Ccw = walk_face_ccw(Edge, Etab, Face, Edge, []),
215
Edge = walk_face_cw(Edge, Etab, Face, Ccw),
216
[V|Vs] = lists:sort(Ccw),
217
validate_face_vertices(Vs, V).
219
validate_face_vertices([V|_], V) ->
220
erlang:error(repeated_vertex);
221
validate_face_vertices([_], _) ->
223
validate_face_vertices([V|Vs], _) ->
224
validate_face_vertices(Vs, V).
226
walk_face_ccw(LastEdge, _, _, LastEdge, [_|_]=Acc) -> Acc;
227
walk_face_ccw(Edge, Etab, Face, LastEdge, Acc) ->
228
case gb_trees:get(Edge, Etab) of
229
#edge{ve=V,lf=Face,ltpr=Next} ->
230
walk_face_ccw(Next, Etab, Face, LastEdge, [V|Acc]);
231
#edge{vs=V,rf=Face,rtpr=Next} ->
232
walk_face_ccw(Next, Etab, Face, LastEdge, [V|Acc])
235
walk_face_cw(Edge, _, _, []) -> Edge;
236
walk_face_cw(Edge, Etab, Face, [V|Vs]) ->
237
case gb_trees:get(Edge, Etab) of
238
#edge{vs=V,lf=Face,ltsu=Next} ->
239
walk_face_cw(Next, Etab, Face, Vs);
240
#edge{ve=V,rf=Face,rtsu=Next} ->
241
walk_face_cw(Next, Etab, Face, Vs)
244
validate_vertex_tab(#we{es=Etab,vc=Vct}) ->
245
lists:foreach(fun({V,Edge}) ->
246
case gb_trees:get(Edge, Etab) of
250
end, gb_trees:to_list(Vct)).