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