1%%
2%%  wings_subdiv.erl --
3%%
4%%     This module implements the Smooth command for objects and faces.
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_subdiv).
15-export([subdiv/1, smooth/1,smooth/5,subdiv/5,inc_smooth/2,
16	 get_proxy_info/3, inc_smooth/4]).
17
18-export([smooth_faces_htab/1]).
19
20-include("wings.hrl").
21
22-import(lists, [reverse/1,reverse/2,sort/1,foldl/3]).
23
24%%% Simple subdivision
25
26%% subdiv(We) -> We'
27%%  Sub-divide an entire object.
28%%
29subdiv(We) ->
30    {Fs,Htab} = smooth_faces_htab(We),
31    #we{vp=Vtab,es=Etab} = We,
32    Vs = wings_util:array_keys(Vtab),
33    Es = wings_util:array_keys(Etab),
34    subdiv(true, Fs, Vs, Es, Htab, We).
35
36subdiv(Fs, Vs, Es, Htab, We) ->
37    subdiv(false, Fs, Vs, Es, Htab, We).
38
39subdiv(EntireObject, Fs, Vs, Es, Htab, #we{vp=Vp,next_id=Id}=We0) ->
40    wings_pb:start(?__(1,"subdividing")),
41    wings_pb:update(0.05, ?__(2,"calculating face centers")),
42    FacePos0 = face_centers(Fs, We0),
43
44    %% First do all topological changes to the edge table.
45    wings_pb:update(0.20, ?__(3,"cutting edges")),
46    We1 = cut_edges(Es, Htab, We0#we{vc=undefined}),
47    wings_pb:update(0.25, ?__(4,"updating materials")),
48    We2 = smooth_materials(Fs, FacePos0, We1),
49    wings_pb:update(0.47, ?__(5,"creating new faces")),
50    {We3,Hide} = smooth_faces(FacePos0, Id, We2),
51    wings_pb:update(0.60, ?__(6,"moving vertices")),
52
53    %% Now calculate all vertex positions.
54    {RevUpdatedVs,Mid} =
55	case EntireObject of
56	    true ->
57		soft_update_edge_vs_all(We0, Vp, Id);
58	    false ->
59		soft_update_edge_vs_some(Es, We0, Vp, Id)
60	end,
61    FacePos = gb_trees:from_orddict([{F,Pos} || {F,{Pos,_,_}} <- FacePos0]),
62    VtabTail = smooth_new_vs(FacePos0, Mid, RevUpdatedVs),
63    Vtab = soft_move_orig(EntireObject, Vs, FacePos, Htab, We0, VtabTail),
64
65    %% Done, except that we'll need to re-hide any hidden faces
66    %% and rebuild tables.
67    wings_pb:update(1.0, ?__(7,"finishing")),
68    We4 = We3#we{vp=Vtab},
69    We = if
70	     Hide =:= [] ->
71		 wings_we:rebuild(We4);
72	     true ->
73		 wings_we:hide_faces(Hide, We4) %Will force a rebuild.
74	 end,
75    wings_pb:done(We).
76
77%%% The Catmull-Clark subdivision algorithm is used, with
78%%% Tony DeRose's extensions for creases.
79
80%% smooth(We) -> We'
81%%  Sub-divide an entire object.
82%%
83smooth(We) ->
84    {Fs,Htab} = smooth_faces_htab(We),
85    #we{vp=Vtab,es=Etab} = We,
86    Vs = wings_util:array_keys(Vtab),
87    Es = wings_util:array_keys(Etab),
88    smooth(true, Fs, Vs, Es, Htab, We).
89
90%% smooth([Face], [Vertex], [Edge], [HardEdge], We) -> We'
91%%  Sub-divide only one or more regions of faces.
92%%
93smooth(Fs, Vs, Es, Htab, We) ->
94    smooth(false, Fs, Vs, Es, Htab, We).
95
96%% smooth(EntireObject, [Face], [Vertex], [Edge], [HardEdge], We) -> We'
97%%  Sub-divide only one or more regions of faces. EntireObject
98%%  is a boolean.
99%%
100smooth(EntireObject, Fs, Vs, Es, Htab, #we{vp=Vp,next_id=Id}=We0) ->
101    wings_pb:start(?__(1,"smoothing")),
102    wings_pb:update(0.05, ?__(2,"calculating face centers")),
103    FacePos0 = face_centers(Fs, We0),
104
105    %% First do all topological changes to the edge table.
106    wings_pb:update(0.20, ?__(3,"cutting edges")),
107    We1 = cut_edges(Es, Htab, We0#we{vc=undefined}),
108    wings_pb:update(0.25, ?__(4,"updating materials")),
109    We2 = smooth_materials(Fs, FacePos0, We1),
110    wings_pb:update(0.47, ?__(5,"creating new faces")),
111    {We3,Hide} = smooth_faces(FacePos0, Id, We2),
112    wings_pb:update(0.60, ?__(6,"moving vertices")),
113
114    %% Now calculate all vertex positions.
115    FacePos = gb_trees:from_orddict([{F,Pos} || {F,{Pos,_,_}} <- FacePos0]),
116    {RevUpdatedVs,Mid} =
117	case EntireObject of
118	    true ->
119		update_edge_vs_all(We0, FacePos, Htab, Vp, Id);
120	    false ->
121		update_edge_vs_some(Es, We0, FacePos, Htab, Vp, Id)
122	end,
123    VtabTail = smooth_new_vs(FacePos0, Mid, RevUpdatedVs),
124    Vtab = smooth_move_orig(EntireObject, Vs, FacePos, Htab, We0, VtabTail),
125
126    %% Done, except that we'll need to re-hide any hidden faces
127    %% and rebuild tables.
128    wings_pb:update(1.0, ?__(7,"finishing")),
129    We4 = We3#we{vp=Vtab},
130    We = if
131	     Hide =:= [] ->
132		 wings_we:rebuild(We4);
133	     true ->
134		 wings_we:hide_faces(Hide, We4) %Will force a rebuild.
135	 end,
136    wings_pb:done(We).
137
138inc_smooth(#we{vp=Vp,next_id=Next}=We0, Smoothed) ->
139    case wings_va:any_update(We0, Smoothed) of
140	true ->
141	    %% Vertex attributes may have changed. Fall back to
142	    %% complete smooth.
143	    smooth(We0);
144	false ->
145	    %% Do an incremental smooth.
146	    {Faces,Htab} = smooth_faces_htab(We0),
147	    FacePos0 = face_centers(Faces, We0),
148	    FacePos = gb_trees:from_orddict([{F,Pos} ||
149						{F,{Pos,_,_}} <- FacePos0]),
150	    {RevUpdatedVs,Mid} = update_edge_vs_all(We0, FacePos, Htab, Vp, Next),
151	    VtabTail = smooth_new_vs(FacePos0, Mid, RevUpdatedVs),
152	    Vtab = smooth_move_orig(true, wings_util:array_keys(Vp),
153				    FacePos, Htab, We0, VtabTail),
154	    Smoothed#we{vp=Vtab}
155    end.
156
157get_proxy_info(DynVs, UpdateVs, #we{es=Etab,next_id=Next}=We0) ->
158    {Faces,Htab} = smooth_faces_htab(We0),
159    FacePos0  = face_centers(Faces, We0),
160    FacePos = gb_trees:from_orddict([{F,Pos} || {F,{Pos,_,_}} <- FacePos0]),
161    Elist = array:sparse_to_orddict(Etab),
162    {EdgeSplit,Mid} = get_edge_vs(Elist, UpdateVs, Htab, Next, []),
163    SmoothNew = get_new_vs(FacePos0, Mid, UpdateVs, []),
164    OrigVs = get_orig_vs(DynVs, get_orig_vs_fun(Htab), We0, []),
165    {FacePos,EdgeSplit,SmoothNew,OrigVs}.
166
167inc_smooth(#we{vp=Vtab}=We, Faces, {FacePos0,EdgeSplit,SmoothNew,OrigVs},
168	   Smoothed = #we{vp=Vp0}) ->
169    FacePos = foldl(fun(Face, FacePos) ->
170			    Pos = wings_face:vertex_positions(Face, We),
171			    gb_trees:update(Face, e3d_vec:average(Pos), FacePos)
172		    end, FacePos0, Faces),
173    Vp1 = update_edge_vs(EdgeSplit, Vtab, FacePos, Vp0),
174    Vp2 = update_new_vs(SmoothNew, FacePos, Vp1),
175    Vp3 = update_orig_vs(OrigVs, Vtab, FacePos, Vp2),
176    Smoothed#we{vp=Vp3}.
177
178smooth_faces_htab(#we{mirror=none,fs=Ftab,he=Htab,holes=[]}) ->
179    Faces = gb_trees:keys(Ftab),
180    {Faces,Htab};
181smooth_faces_htab(#we{mirror=Mirror,fs=Ftab,he=Htab,holes=Holes}=We) ->
182    Exclude = case Mirror of
183		  none -> Holes;
184		  _ -> ordsets:add_element(Mirror, Holes)
185	      end,
186    Faces = ordsets:subtract(gb_trees:keys(Ftab), Exclude),
187    He0 = wings_face:to_edges(Exclude, We),
188    He = gb_sets:union(gb_sets:from_list(He0), Htab),
189    {Faces,He}.
190
191%%%
192%%% Calculation of face centers.
193%%%
194
195face_centers(Faces, We) ->
196    face_centers(Faces, We, []).
197
198face_centers([Face|Fs], We, Acc) ->
199    Attrs = wings_va:face_mixed_attrs(Face, We),
200    case wings_face:vertex_positions(Face, We) of
201	[_,_] ->
202	    wings_u:error_msg(?__(1,"Face ") ++ integer_to_list(Face) ++
203			  ?__(2," has only two edges."));
204	Positions ->
205	    Center = wings_util:share(e3d_vec:average(Positions)),
206	    face_centers(Fs, We, [{Face,{Center,Attrs,length(Positions)}}|Acc])
207    end;
208face_centers([], _We, Acc) -> reverse(Acc).
209
210%%%
211%%% Updating of the topology (edge and hard edge tables).
212%%%
213
214cut_edges(Es, Hard, #we{es=Etab0,he=Htab0,next_id=Id0}=We0) ->
215    {Id,Etab,Htab} = cut_edges_1(Es, Hard, Id0, Etab0, Htab0),
216    We = We0#we{es=Etab,he=Htab,next_id=Id},
217    case wings_va:any_attributes(We) of
218	false -> We;
219	true -> cut_edges_attrs(Es, Id0, We0, We)
220    end.
221
222cut_edges_1([Edge|Es], Hard, NewEdge, Etab0, Htab0) ->
223    Rec = array:get(Edge, Etab0),
224    Etab = fast_cut(Edge, Rec, NewEdge, Etab0),
225    case gb_sets:is_member(Edge, Hard) of
226	true ->
227	    Htab = case gb_sets:is_member(Edge, Htab0) of
228		       true -> gb_sets:insert(NewEdge, Htab0);
229		       false -> Htab0
230		   end,
231	    cut_edges_1(Es, Hard, NewEdge+1, Etab, Htab);
232	false ->
233	    cut_edges_1(Es, Hard, NewEdge+1, Etab, Htab0)
234    end;
235cut_edges_1([], _Hard, Id, Etab, Htab) ->
236    {Id,Etab,Htab}.
237
238fast_cut(Edge, Template, NewV=NewEdge, Etab0) ->
239    #edge{ltpr=EdgeA,rtsu=EdgeB} = Template,
240    NewEdgeRec = Template#edge{vs=NewV,ltsu=Edge,rtpr=Edge},
241    Etab1 = array:set(NewEdge, NewEdgeRec, Etab0),
242    EdgeRec = Template#edge{ve=NewV,rtsu=NewEdge,ltpr=NewEdge},
243    Etab2 = array:set(Edge, EdgeRec, Etab1),
244    Etab = wings_edge:patch_edge(EdgeA, NewEdge, Edge, Etab2),
245    wings_edge:patch_edge(EdgeB, NewEdge, Edge, Etab).
246
247cut_edges_attrs([Edge|Es], NewEdge, #we{es=Etab}=OrigWe, We0) ->
248    #edge{lf=Lf,rf=Rf,ltpr=Ltpr,rtpr=Rtpr} = array:get(Edge, Etab),
249
250    LeftAttrA = wings_va:edge_attrs(Edge, left, OrigWe),
251    LeftAttrB =	wings_va:edge_attrs(Ltpr, Lf, OrigWe),
252    AttrMidLeft = wings_va:average_attrs(LeftAttrA, LeftAttrB),
253
254    AttrRightA = wings_va:edge_attrs(Edge, right, OrigWe),
255    AttrRightB = wings_va:edge_attrs(Rtpr, Rf, OrigWe),
256    AttrMidRight = wings_va:average_attrs(AttrRightA, AttrRightB),
257
258    We1 = wings_va:set_edge_attrs(Edge, right, AttrMidRight, We0),
259    We = wings_va:set_both_edge_attrs(NewEdge, AttrMidLeft, AttrRightA, We1),
260
261    cut_edges_attrs(Es, NewEdge+1, OrigWe, We);
262cut_edges_attrs([], _, _, We) -> We.
263
264smooth_faces(FacePos, Id, We0) ->
265    We1 = smooth_faces_1(FacePos, Id, We0),
266    We = case wings_va:any_attributes(We1) of
267	     false -> We1;
268	     true -> smooth_faces_attrs(FacePos, Id, We0, We1)
269	 end,
270    case wings_we:is_open(We0) of
271	false -> {We,[]};
272	true -> {We,smooth_faces_hide(FacePos, We0)}
273    end.
274
275smooth_faces_1([{Face,{_,_,NumIds}}|Fs], Id, #we{es=Etab0}=We0) ->
276    {Ids,We} = wings_we:new_wrap_range(NumIds, 1, We0),
277    NewV = wings_we:id(0, Ids),
278    Fun = smooth_edge_fun(Face, NewV, Id),
279    {Etab,_} = face_fold(Fun, {Etab0,Ids}, Face, We),
280    smooth_faces_1(Fs, Id, We#we{es=Etab});
281smooth_faces_1([], _, We) ->
282    We#we{fs=undefined}.
283
284smooth_edge_fun(Face, NewV, Id) ->
285    fun(Edge, Rec0, Next, {Etab0,Ids0}) ->
286	    LeftEdge = RFace = wings_we:id(0, Ids0),
287	    NewEdge = LFace = wings_we:id(1, Ids0),
288	    RightEdge = wings_we:id(2, Ids0),
289	    case Rec0 of
290		#edge{ve=Vtx,rf=Face} when Vtx >= Id ->
291		    Ids = Ids0,
292		    Rec = Rec0#edge{rf=RFace,rtsu=NewEdge},
293		    NewErec = #edge{vs=Vtx,ve=NewV,
294				    rf=RFace,lf=LFace,
295				    rtpr=Edge,rtsu=LeftEdge,
296				    ltpr=RightEdge,ltsu=Next},
297		    Etab1 = array:set(NewEdge, NewErec, Etab0);
298		#edge{vs=Vtx,lf=Face} when Vtx >= Id ->
299		    Ids = Ids0,
300		    Rec = Rec0#edge{lf=RFace,ltsu=NewEdge},
301		    NewErec = #edge{vs=Vtx,ve=NewV,
302				    rf=RFace,lf=LFace,
303				    rtpr=Edge,rtsu=LeftEdge,
304				    ltpr=RightEdge,ltsu=Next},
305		    Etab1 = array:set(NewEdge, NewErec, Etab0);
306		#edge{vs=Vtx,rf=Face} when Vtx >= Id ->
307		    Rec = Rec0#edge{rf=LFace,rtpr=NewEdge},
308		    Etab1 = Etab0,
309		    Ids = wings_we:bump_id(Ids0);
310		#edge{ve=Vtx,lf=Face} when Vtx >= Id ->
311		    Rec = Rec0#edge{lf=LFace,ltpr=NewEdge},
312		    Etab1 = Etab0,
313		    Ids = wings_we:bump_id(Ids0)
314	    end,
315	    Etab = array:set(Edge, Rec, Etab1),
316	    {Etab,Ids}
317    end.
318
319smooth_faces_attrs([{Face,{_,Color,NumIds}}|Fs], Id, OrigWe0, We0) ->
320    {Ids,OrigWe} = wings_we:new_wrap_range(NumIds, 1, OrigWe0),
321    Fun = smooth_edge_fun_attrs(Face, Color, Id),
322    {We,_} = face_fold(Fun, {We0,Ids}, Face, OrigWe),
323    smooth_faces_attrs(Fs, Id, OrigWe, We);
324smooth_faces_attrs([], _, _, We) -> We.
325
326smooth_edge_fun_attrs(Face, Color, Id) ->
327    fun(Edge, Rec0, _Next, {We0,Ids0}) ->
328	    NewEdge = wings_we:id(1, Ids0),
329	    case Rec0 of
330		#edge{ve=Vtx,rf=Face} when Vtx >= Id ->
331		    Ids = Ids0,
332		    OldAttrs = wings_va:edge_attrs(Edge, right, We0),
333		    We = wings_va:set_both_edge_attrs(NewEdge, OldAttrs, Color, We0);
334		#edge{vs=Vtx,lf=Face} when Vtx >= Id ->
335		    Ids = Ids0,
336		    OldAttrs = wings_va:edge_attrs(Edge, left, We0),
337		    We = wings_va:set_both_edge_attrs(NewEdge, OldAttrs, Color, We0);
338		#edge{vs=Vtx,rf=Face} when Vtx >= Id ->
339		    We = We0,
340		    Ids = wings_we:bump_id(Ids0);
341		#edge{ve=Vtx,lf=Face} when Vtx >= Id ->
342		    We = We0,
343		    Ids = wings_we:bump_id(Ids0)
344	    end,
345	    {We,Ids}
346    end.
347
348smooth_faces_hide(Fs, #we{next_id=Id}) ->
349    smooth_faces_hide_1(Fs, Id, []).
350
351smooth_faces_hide_1([{Face,{_,_,NumIds}}|Fs], Id, Acc) when Face >= 0 ->
352    smooth_faces_hide_1(Fs, Id+NumIds, Acc);
353smooth_faces_hide_1([{_,{_,_,NumIds}}|Fs], Id, Acc0) ->
354    Acc = smooth_faces_hide_2(NumIds, Id, Acc0),
355    smooth_faces_hide_1(Fs, Id+NumIds, Acc);
356smooth_faces_hide_1([], _, Acc) -> Acc.
357
358smooth_faces_hide_2(0, _, Acc) -> Acc;
359smooth_faces_hide_2(N, Id, Acc) -> smooth_faces_hide_2(N-1, Id+1, [Id|Acc]).
360
361face_fold(F, Acc, Face, #we{es=Etab,fs=Ftab}) ->
362    Edge = gb_trees:get(Face, Ftab),
363    face_fold(Edge, Etab, F, Acc, Face, Edge, not_done).
364
365face_fold(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
366face_fold(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
367    case array:get(Edge, Etab) of
368	#edge{lf=Face,ltsu=NextEdge}=E ->
369	    Acc = F(Edge, E, NextEdge, Acc0),
370	    face_fold(NextEdge, Etab, F, Acc, Face, LastEdge, done);
371	#edge{rf=Face,rtsu=NextEdge}=E ->
372	    Acc = F(Edge, E, NextEdge, Acc0),
373	    face_fold(NextEdge, Etab, F, Acc, Face, LastEdge, done)
374    end.
375
376%%
377%% XXX This is ugly. Here the materials are directly manpulated.
378%%
379smooth_materials(_, _, #we{mat=Mat}=We) when is_atom(Mat) -> We;
380smooth_materials(Fs, FacePos, #we{fs=Ftab,mat=Mat0}=We) ->
381    case length(Fs) =:= gb_trees:size(Ftab) of
382	true ->				  %We are smoothing all faces.
383	    smooth_materials_1(Mat0, FacePos, We, []);
384	false ->		 %Must pick up the faces not smoothed.
385	    Mat1 = sofs:from_external(Mat0, [{face,mat}]),
386	    Changed = sofs:from_external(Fs, [face]),
387	    {Mat2,Keep0} = sofs:partition(1, Mat1, Changed),
388	    Mat = sofs:to_external(Mat2),
389	    Keep = sofs:to_external(Keep0),
390	    smooth_materials_1(Mat, FacePos, We, Keep)
391    end.
392
393smooth_materials_1(Fmat, Fpos, #we{next_id=Id}=We, Keep) ->
394    Mat = smooth_materials_2(Fmat, Fpos, Id, Keep),
395    We#we{mat=sort(Mat)}.
396
397smooth_materials_2([{F,Mat}|Fs], [{F,{_,_,N}}|Fpos], Face, Acc0) ->
398    NextFace = Face+N,
399    Acc = smooth_materials_3(Mat, NextFace, Face, Acc0),
400    smooth_materials_2(Fs, Fpos, NextFace, Acc);
401smooth_materials_2([], [], _, Acc) -> Acc.
402
403smooth_materials_3(_, Face, Face, Acc) -> Acc;
404smooth_materials_3(Mat, NextFace, Face, Acc) ->
405    smooth_materials_3(Mat, NextFace, Face+1, [{Face,Mat}|Acc]).
406
407%%%
408%%% Moving of vertices.
409%%%
410
411soft_move_orig(EntireObject, Vs, FacePos, Htab, #we{vp=Vtab}=We, VtabTail) ->
412    MoveFun = smooth_move_orig_fun(Vtab, FacePos, Htab),
413    RevVtab =
414	case EntireObject of
415	    true ->
416		soft_move_orig_all(array:sparse_to_orddict(Vtab), MoveFun, We, []);
417	    _ ->
418		soft_move_orig_some(Vs, array:sparse_to_orddict(Vtab), MoveFun, We, [])
419	end,
420    array:from_orddict(reverse(RevVtab, VtabTail)).
421
422soft_move_orig_all([{V,Pos}|Vs], MoveFun, We, Acc) ->
423    soft_move_orig_all(Vs, MoveFun, We, [{V,Pos}|Acc]);
424soft_move_orig_all([], _FacePos, _MoveFun, Acc) -> Acc.
425
426soft_move_orig_some([V|Vs], [{V,Pos}|Vs2], MoveFun, We, Acc) ->
427    soft_move_orig_some(Vs, Vs2, MoveFun, We, [{V,Pos}|Acc]);
428soft_move_orig_some(Vs, [Pair|Vs2], MoveFun, We, Acc) ->
429    soft_move_orig_some(Vs, Vs2, MoveFun, We, [Pair|Acc]);
430soft_move_orig_some([], [], _, _, Acc) -> Acc;
431soft_move_orig_some([], Vs2, _, _, Acc) -> reverse(Vs2, Acc).
432
433
434smooth_move_orig(EntireObject, Vs, FacePos, Htab, #we{vp=Vtab}=We, VtabTail) ->
435    MoveFun = smooth_move_orig_fun(Vtab, FacePos, Htab),
436    RevVtab =
437	case EntireObject of
438	    true ->
439		smooth_move_orig_all(array:sparse_to_orddict(Vtab), MoveFun, We, []);
440	    _ ->
441		smooth_move_orig_some(Vs, array:sparse_to_orddict(Vtab), MoveFun, We, [])
442	end,
443    array:from_orddict(reverse(RevVtab, VtabTail)).
444
445smooth_move_orig_all([{V,Pos0}|Vs], MoveFun, We, Acc) ->
446    Pos = smooth_move_orig_1(V, Pos0, MoveFun, We),
447    smooth_move_orig_all(Vs, MoveFun, We, [{V,Pos}|Acc]);
448smooth_move_orig_all([], _FacePos, _MoveFun, Acc) -> Acc.
449
450smooth_move_orig_some([V|Vs], [{V,Pos0}|Vs2], MoveFun, We, Acc) ->
451    Pos = smooth_move_orig_1(V, Pos0, MoveFun, We),
452    smooth_move_orig_some(Vs, Vs2, MoveFun, We, [{V,Pos}|Acc]);
453smooth_move_orig_some(Vs, [Pair|Vs2], MoveFun, We, Acc) ->
454    smooth_move_orig_some(Vs, Vs2, MoveFun, We, [Pair|Acc]);
455smooth_move_orig_some([], [], _, _, Acc) -> Acc;
456smooth_move_orig_some([], Vs2, _, _, Acc) -> reverse(Vs2, Acc).
457
458smooth_move_orig_1(V, S, MoveFun, We) ->
459    {_,Ps0,Hard} = wings_vertex:fold(MoveFun, {V,[],[]}, V, We),
460    case length(Hard) of
461	NumHard when NumHard < 2 ->
462	    Ps = e3d_vec:add(Ps0),
463	    {A,B} = case length(Ps0) of
464			2*3 -> {1/9,1/3};
465			2*4 -> {1/16,2/4};
466			2*5 -> {1/25,3/5};
467			N0 ->
468			    N = N0 bsr 1,
469			    {1.0/(N*N),(N-2.0)/N}
470		    end,
471	    Pos = e3d_vec:add_prod(e3d_vec:mul(Ps, A), S, B),
472	    wings_util:share(Pos);
473	NumHard when NumHard =:= 2 ->
474	    Pos0 = e3d_vec:add([e3d_vec:mul(S, 6.0)|Hard]),
475	    Pos = e3d_vec:mul(Pos0, 1/8),
476	    wings_util:share(Pos);
477	_ThreeOrMore -> S
478    end.
479
480smooth_move_orig_fun(Vtab, FacePos, Htab) ->
481    case gb_sets:is_empty(Htab) of
482	true ->
483	    fun(_Edge, Face, Erec, {V,Ps,_}) ->
484		    %% No hard edges imply that all faces can be found
485		    %% in the FacePos table. Therefore gb_trees:get/2 is safe.
486		    OPos = wings_vertex:other_pos(V, Erec, Vtab),
487		    FPos = gb_trees:get(Face, FacePos),
488		    {V,[OPos,FPos|Ps],[]}
489	    end;
490	false ->
491	    fun(Edge, Face, Erec, {V,Ps0,Hard0}) ->
492		    OPos = wings_vertex:other_pos(V, Erec, Vtab),
493		    FPos = case gb_trees:lookup(Face, FacePos) of
494			       none -> none;
495			       {value,FPos0} -> FPos0
496			   end,
497		    Ps = [FPos,OPos|Ps0],
498		    Es = case gb_sets:is_member(Edge, Htab) of
499			     true -> [OPos|Hard0];
500			     false -> Hard0
501			 end,
502		    {V,Ps,Es}
503	    end
504    end.
505
506get_orig_vs_fun(Htab) ->
507    case gb_sets:is_empty(Htab) of
508	true ->
509	    fun(_Edge, Face, Erec, {V,Ps,_}) ->
510		    Other = wings_vertex:other(V, Erec),
511		    {V,[Other,Face|Ps],[]}
512	    end;
513	false ->
514	    fun(Edge, Face, Erec, {V,Ps0,Hard0}) ->
515		    Other = wings_vertex:other(V, Erec),
516		    Ps = [Other,Face|Ps0],
517		    Es = case gb_sets:is_member(Edge, Htab) of
518			     true -> [Other|Hard0];
519			     false -> Hard0
520			 end,
521		    {V,Ps,Es}
522	    end
523    end.
524
525get_orig_vs([V|Vs], Fun, We, Acc) ->
526    {_, Ps,Hard} = wings_vertex:fold(Fun, {V,[],[]}, V, We),
527    case length(Hard) of
528	NumHard when NumHard < 2 ->
529	    {A,B} = case length(Ps) of
530			2*3 -> {1/9,1/3};
531			2*4 -> {1/16,2/4};
532			2*5 -> {1/25,3/5};
533			N0 ->
534			    N = N0 bsr 1,
535			    {1.0/(N*N),(N-2.0)/N}
536		    end,
537	    get_orig_vs(Vs, Fun, We, [{V,Ps,A,B}|Acc]);
538	NumHard when NumHard =:= 2 ->
539	    get_orig_vs(Vs, Fun, We, [{V,Hard}|Acc]);
540	_ThreeOrMore ->
541	    get_orig_vs(Vs, Fun, We, Acc)
542    end;
543get_orig_vs([],_, _, Acc) ->
544    Acc.
545
546update_orig_vs([{V,Ps0,A,B}|Vs], Vtab, Ftab, Vpos) ->
547    S   = array:get(V, Vtab),
548    Ps  = add_positions(Ps0, Vtab, Ftab, {0.0,0.0,0.0}),
549    Pos = e3d_vec:add_prod(e3d_vec:mul(Ps, A), S, B),
550    update_orig_vs(Vs, Vtab, Ftab, array:set(V,Pos, Vpos));
551update_orig_vs([{V,Hard0}|Vs], Vtab, Ftab, Vpos) ->
552    S    = array:get(V, Vtab),
553    Hard = [array:get(H, Vtab) || H <- Hard0],
554    Pos0 = e3d_vec:add([e3d_vec:mul(S, 6.0)|Hard]),
555    Pos  = e3d_vec:mul(Pos0, 1/8),
556    update_orig_vs(Vs, Vtab, Ftab, array:set(V,Pos, Vpos));
557update_orig_vs([], _, _, Vpos) -> Vpos.
558
559add_positions([V,F|Rest], Vtab, Ftab, Sum0) ->
560    Sum1 = e3d_vec:add(array:get(V, Vtab), Sum0),
561    Sum  = e3d_vec:add(gb_trees:get(F, Ftab), Sum1),
562    add_positions(Rest, Vtab, Ftab, Sum);
563add_positions([],_,_,Sum) -> Sum.
564
565%% Update the position for the vertex that was created in the middle
566%% of each original edge.
567
568soft_update_edge_vs_all(#we{es=Etab}, Vtab, V) ->
569    soft_update_edge_vs_all(array:sparse_to_orddict(Etab), Vtab, V, []).
570
571soft_update_edge_vs_all([{_,Rec}|Es], Vtab, V, Acc) ->
572    Pos = soft_update_edge_vs_1(Rec, Vtab),
573    soft_update_edge_vs_all(Es, Vtab, V+1, [{V,Pos}|Acc]);
574soft_update_edge_vs_all([], _, V, Acc) ->
575    {Acc,V}.
576
577soft_update_edge_vs_some(Es, #we{es=Etab}, Vtab, V) ->
578    soft_update_edge_vs_some(Es, Etab, Vtab, V, []).
579
580soft_update_edge_vs_some([E|Es], Etab, Vtab, V, Acc) ->
581    Rec = array:get(E, Etab),
582    Pos = soft_update_edge_vs_1(Rec, Vtab),
583    soft_update_edge_vs_some(Es, Etab, Vtab, V+1, [{V,Pos}|Acc]);
584soft_update_edge_vs_some([], _, _, V, Acc) ->
585    {Acc,V}.
586
587soft_update_edge_vs_1(Rec, Vtab) ->
588    #edge{vs=Va,ve=Vb} = Rec,
589    e3d_vec:average(array:get(Va, Vtab), array:get(Vb, Vtab)).
590
591
592update_edge_vs_all(#we{es=Etab}, FacePos, Hard, Vtab, V) ->
593    update_edge_vs_all(array:sparse_to_orddict(Etab),
594		       FacePos, Hard, Vtab, V, []).
595
596update_edge_vs_all([{Edge,Rec}|Es], FacePos, Hard, Vtab, V, Acc) ->
597    Pos = update_edge_vs_1(Edge, Hard, Rec, FacePos, Vtab),
598    update_edge_vs_all(Es, FacePos, Hard, Vtab, V+1, [{V,Pos}|Acc]);
599update_edge_vs_all([], _, _, _, V, Acc) ->
600    {Acc,V}.
601
602update_edge_vs_some(Es, #we{es=Etab}, FacePos, Hard, Vtab, V) ->
603    update_edge_vs_some(Es, Etab, FacePos, Hard, Vtab, V, []).
604
605update_edge_vs_some([E|Es], Etab, FacePos, Hard, Vtab, V, Acc) ->
606    Rec = array:get(E, Etab),
607    Pos = update_edge_vs_1(E, Hard, Rec, FacePos, Vtab),
608    update_edge_vs_some(Es, Etab, FacePos, Hard, Vtab, V+1, [{V,Pos}|Acc]);
609update_edge_vs_some([], _, _, _, _, V, Acc) ->
610    {Acc,V}.
611
612update_edge_vs_1(Edge, Hard, Rec, FacePos, Vtab) ->
613    case gb_sets:is_member(Edge, Hard) of
614	true ->
615	    #edge{vs=Va,ve=Vb} = Rec,
616	    e3d_vec:average(array:get(Va, Vtab), array:get(Vb, Vtab));
617	false ->
618	    #edge{vs=Va,ve=Vb,lf=Lf,rf=Rf} = Rec,
619	    LfPos = gb_trees:get(Lf, FacePos),
620	    RfPos = gb_trees:get(Rf, FacePos),
621	    Pos0 = e3d_vec:average(array:get(Va, Vtab),
622				   array:get(Vb, Vtab),
623				   LfPos, RfPos),
624	    wings_util:share(Pos0)
625    end.
626
627get_edge_vs([{Edge,Rec}|Es], Update, Hard, V, Acc) ->
628    case gb_sets:is_member(V, Update) of
629	true ->
630	    case gb_sets:is_member(Edge,Hard) of
631		true ->
632		    #edge{vs=Va,ve=Vb} = Rec,
633		    get_edge_vs(Es,Update,Hard,V+1,[{V,Va,Vb}|Acc]);
634		false ->
635		    #edge{vs=Va,ve=Vb,lf=Lf,rf=Rf} = Rec,
636		    get_edge_vs(Es,Update,Hard,V+1,
637				[{V,Va,Vb,Lf,Rf}|Acc])
638	    end;
639	false ->
640	    get_edge_vs(Es,Update,Hard,V+1,Acc)
641    end;
642get_edge_vs([], _, _, V, Acc) -> {Acc,V}.
643
644update_edge_vs([{V,Va,Vb}|Vs], Vtab, Ftab, Vpos) ->
645    Pos = e3d_vec:average(array:get(Va, Vtab), array:get(Vb, Vtab)),
646    update_edge_vs(Vs, Vtab, Ftab, array:set(V, Pos, Vpos));
647update_edge_vs([{V,Va,Vb,Lf,Rf}|Vs], Vtab, Ftab, Vpos) ->
648    LfPos = gb_trees:get(Lf, Ftab),
649    RfPos = gb_trees:get(Rf, Ftab),
650    Pos = e3d_vec:average(array:get(Va, Vtab),
651			  array:get(Vb, Vtab),
652			  LfPos, RfPos),
653    update_edge_vs(Vs, Vtab, Ftab, array:set(V, Pos, Vpos));
654update_edge_vs([], _, _, Vpos) -> Vpos.
655
656smooth_new_vs([{_,{Center,_,NumIds}}|Fs], V, Acc) ->
657    smooth_new_vs(Fs, V+NumIds, [{V,Center}|Acc]);
658smooth_new_vs([], _, Acc) -> reverse(Acc).
659
660get_new_vs([{Face,{_,_,NumIds}}|Fs], V, Upd, Acc) ->
661    case gb_sets:is_member(V, Upd) of
662	true ->  get_new_vs(Fs, V+NumIds, Upd, [{V,Face}|Acc]);
663	false -> get_new_vs(Fs, V+NumIds, Upd, Acc)
664    end;
665get_new_vs([], _, _, Acc) -> Acc.
666
667update_new_vs([{V,Face}|Vs], Ftab, Vpos) ->
668    Pos = gb_trees:get(Face, Ftab),
669    update_new_vs(Vs, Ftab, array:set(V, Pos, Vpos));
670update_new_vs([], _, Vpos) -> Vpos.
671