1%% 2%% wings_extrude_edge.erl -- 3%% 4%% This module contains the Extrude (edge), Bevel (face/edge) and 5%% Bump commands. (All based on edge extrusion.) 6%% 7%% Copyright (c) 2001-2011 Bjorn Gustavsson 8%% 9%% See the file "license.terms" for information on usage and redistribution 10%% of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11%% 12%% $Id$ 13%% 14 15-module(wings_extrude_edge). 16-export([bump/1,bevel/1,bevel_faces/1,extrude/2,crease/1,extrude_edges/3]). 17 18-include("wings.hrl"). 19-import(lists, [foldl/3,reverse/1]). 20 21-define(DEFAULT_EXTRUDE_DIST, 0.2). 22-define(BEVEL_EXTRUDE_DIST_KLUDGE, 0.0001). 23 24-type edge_num() :: wings_edge:edge_num(). 25 26%%% 27%%% The Bump command. 28%%% 29 30bump(St0) -> 31 Dist = calc_bump_dist(St0), 32 MF = fun(Fs, We) -> bump(Fs, Dist, We) end, 33 St = wings_sel:map(MF, St0), 34 DF = fun(_, #we{temp=PlusMinus}) -> PlusMinus end, 35 wings_move:plus_minus(normal, DF, St). 36 37calc_bump_dist(St) -> 38 Map = fun calc_bump_dist_1/2, 39 Combine = fun min/2, 40 wings_sel:dfold(Map, Combine, infinity, St) / 2. 41 42calc_bump_dist_1(Faces, We) -> 43 Edges = gb_sets:to_list(wings_edge:from_faces(Faces, We)), 44 min_dist_from_edges(Edges, We). 45 46bump(Faces, Dist, We0) -> 47 Edges = gb_sets:from_list(wings_face:outer_edges(Faces, We0)), 48 {We,_,_,_} = extrude_edges(Edges, Faces, Dist, We0), 49 NewVs = wings_we:new_items_as_ordset(vertex, We0, We), 50 We#we{temp={Faces,NewVs,gb_sets:empty()}}. 51 52%% 53%% The Bevel command (for edges). 54%% 55 56bevel(St0) -> 57 St = wings_sel:map_update_sel(fun bevel_edges/2, face, St0), 58 bevel_drag(St). 59 60bevel_edges(Edges, #we{mirror=MirrorFace}=We0) -> 61 Dist = ?BEVEL_EXTRUDE_DIST_KLUDGE, 62 {We1,OrigVs,_,Forbidden} = extrude_edges(Edges, Dist, We0#we{mirror=none}), 63 We2 = wings_edge:dissolve_edges(Edges, We1), 64 Tv0 = bevel_tv(OrigVs, We2, Forbidden), 65 Tv = scale_tv(Tv0, Dist), 66 We3 = wings_collapse:collapse_vertices(OrigVs, We2), 67 Vtab = bevel_reset_pos(OrigVs, We2, Forbidden, We3#we.vp), 68 We4 = We3#we{vp=Vtab,mirror=MirrorFace}, 69 Limit = bevel_limit(Tv, We4), 70 We = We4#we{temp={Limit,Tv}}, 71 Sel = case gb_sets:is_empty(Forbidden) of 72 true -> 73 wings_we:new_items_as_gbset(face, We0, We); 74 false -> 75 gb_sets:empty() 76 end, 77 {We,Sel}. 78 79%% 80%% The Bevel command (for faces). 81%% 82 83bevel_faces(St0) -> 84 St = wings_sel:map(fun bevel_faces/2, St0), 85 bevel_drag(St). 86 87bevel_faces(Faces, #we{mirror=MirrorFace}=We0) -> 88 Dist = ?BEVEL_EXTRUDE_DIST_KLUDGE, 89 Edges = wings_edge:from_faces(Faces, We0), 90 {We1,OrigVs,_,Forbidden} = extrude_edges(Edges, Dist, We0#we{mirror=none}), 91 case {wings_util:array_entries(We0#we.es),wings_util:array_entries(We1#we.es)} of 92 {Same,Same} -> 93 wings_u:error_msg(?__(1,"Object is too small to bevel.")); 94 {_,_} -> 95 We2 = wings_edge:dissolve_edges(Edges, We1), 96 Tv0 = bevel_tv(OrigVs, We2, Forbidden), 97 Tv = scale_tv(Tv0, Dist), 98 #we{vp=Vtab0} = We3 = wings_collapse:collapse_vertices(OrigVs, We2), 99 Vtab = bevel_reset_pos(OrigVs, We2, Forbidden, Vtab0), 100 We = We3#we{vp=Vtab,mirror=MirrorFace}, 101 Limit = bevel_limit(Tv, We), 102 We#we{temp={Limit,Tv}} 103 end. 104 105%% 106%% Common bevel utilities. 107%% 108 109bevel_drag(St) -> 110 MF = fun(_, #we{temp={Limit,_}}) -> Limit end, 111 RF = fun min/2, 112 Limit = wings_sel:dfold(MF, RF, infinite, St), 113 DF = fun(_, #we{temp={_,Tv}}=We) -> 114 wings_drag:translate_fun(Tv, We) 115 end, 116 wings_drag:fold(DF, [{distance,{0.0,Limit}}], [], St). 117 118bevel_tv(Vs, We, Forbidden) -> 119 foldl(fun(V, A) -> bevel_tv_1(V, We, Forbidden, A) end, [], Vs). 120 121bevel_tv_1(V, We, Forbidden, Acc) -> 122 Center = wings_vertex:pos(V, We), 123 wings_vertex:fold( 124 fun(Edge, _, Rec, Tv0) -> 125 case gb_sets:is_member(Edge, Forbidden) of 126 true -> Tv0; 127 false -> 128 OtherV = wings_vertex:other(V, Rec), 129 Pos = wings_vertex:pos(OtherV, We), 130 Vec = e3d_vec:sub(Pos, Center), 131 [{Vec,[OtherV]}|Tv0] 132 end 133 end, Acc, V, We). 134 135bevel_reset_pos(Vs, We, Forbidden, Vtab) -> 136 foldl(fun(V, A) -> bevel_reset_pos_1(V, We, Forbidden, A) end, Vtab, Vs). 137 138bevel_reset_pos_1(V, We, Forbidden, Vtab) -> 139 Center = wings_vertex:pos(V, We), 140 wings_vertex:fold( 141 fun(Edge, _, Rec, Vt) -> 142 case gb_sets:is_member(Edge, Forbidden) of 143 true -> Vt; 144 false -> 145 OtherV = wings_vertex:other(V, Rec), 146 array:set(OtherV, Center, Vt) 147 end 148 end, Vtab, V, We). 149 150bevel_limit(Tv, We) -> 151 L0 = foldl(fun({Vec,[V]}, A) -> 152 bevel_limit_1(V, Vec, We, A) 153 end, [], Tv), 154 L = wings_util:rel2fam(L0), 155 try 156 bevel_min_limit(L, We, infinite) 157 catch 158 error:badarith -> 159 extrude_problem() 160 end. 161 162bevel_limit_1(V, Vec, #we{vp=Vtab}=We, Acc) -> 163 Pos = array:get(V, Vtab), 164 Data = {Pos,Vec}, 165 wings_vertex:fold(fun(_, _, Rec, A) -> 166 OtherV = wings_vertex:other(V, Rec), 167 [{edge_name(V, OtherV),Data}|A] 168 end, Acc, V, We). 169 170edge_name(Va, Vb) when Va < Vb -> {Va,Vb}; 171edge_name(Va, Vb) -> {Vb,Va}. 172 173bevel_min_limit([{_Edge,[{O1,D1},{O2,D2}]}|Tail], We, Min0) -> 174 %% Find intersection between lines. 175 O2MinusO1 = e3d_vec:sub(O2, O1), 176 D1CrossD2 = e3d_vec:cross(D1, D2), 177 LenD1CrossD2 = e3d_vec:len(D1CrossD2), 178 case LenD1CrossD2*LenD1CrossD2 of 179 Z when Z < (0.01*?DEFAULT_EXTRUDE_DIST*?DEFAULT_EXTRUDE_DIST) -> 180 %% There is no intersection between the lines. 181 case e3d_vec:len(O2MinusO1) of 182 Odist when Odist < 0.000001 -> 183 %% As the vertices are already near each other, 184 %% we will assume that they will be moving apart. 185 bevel_min_limit(Tail, We, Min0); 186 Odist -> 187 D1Len = e3d_vec:len(D1), 188 case e3d_vec:dist(e3d_vec:mul(D1, Odist/D1Len), O2MinusO1) of 189 Dist when Dist < 0.000001 -> 190 %% The vertices will be moved directly towards each 191 %% others. 192 S = Odist / (2*D1Len), 193 bevel_min_limit(Tail, We, min(Min0, S)); 194 _ -> 195 %% The vertices will not meet each other. 196 bevel_min_limit(Tail, We, Min0) 197 end 198 end; 199 SqrLen -> 200 %% There is an intersection. Calculate its parameters. 201 S = e3d_vec:dot(e3d_vec:cross(O2MinusO1, D2), D1CrossD2)/SqrLen, 202 T = e3d_vec:dot(e3d_vec:cross(O2MinusO1, D1), D1CrossD2)/SqrLen, 203 if 204 S > 0, T > 0 -> 205 Min = min(S, min(T, Min0)), 206 bevel_min_limit(Tail, We, Min); 207 true -> 208 %% No intersection in the forward direction. 209 bevel_min_limit(Tail, We, Min0) 210 end 211 end; 212bevel_min_limit([{{Va,Vb},[{_,D1}]}|Tail], #we{vp=Vtab}=We, Min0) -> 213 VaPos = array:get(Va, Vtab), 214 VbPos = array:get(Vb, Vtab), 215 case e3d_vec:len(D1) of 216 DLen when DLen < 0.000001 -> 217 bevel_min_limit(Tail, We, 0.0); 218 DLen -> 219 case e3d_vec:len(e3d_vec:sub(VaPos, VbPos)) / DLen of 220 Min when Min < Min0 -> 221 bevel_min_limit(Tail, We, Min); 222 _ -> 223 bevel_min_limit(Tail, We, Min0) 224 end 225 end; 226bevel_min_limit([], _, Min) -> Min. 227 228-spec extrude_problem() -> no_return(). 229extrude_problem() -> 230 M = ?__(1,"Can't extrude/bevel; two or more vertices are " 231 "probably too near to each other.\n" 232 "Try the Cleanup command."), 233 wings_u:error_msg(M). 234 235%% 236%% Crease command 237%% 238 239crease(St0) -> 240 Dist = calc_extrude_dist(St0), 241 MF = fun(Edges, We0) -> 242 We1 = extrude_1(Edges, Dist, We0), 243 #we{temp={_,NewVs0,F}} = We1, 244 ValidCaps = find_cap_vs(Edges, We0, []), 245 #we{vp=Vtab}=We = foldl(fun(V, We2) -> 246 EndCap = wings_vertex:fold(fun 247 (Edge, _, #edge{lf=Lf,rf=Rf}=E, EndCapAcc) -> 248 case wings_face:vertices(Lf, We2) of 249 3 -> 250 case wings_face:vertices(Rf, We2) of 251 3 -> 252 Other = wings_vertex:other(V, E), 253 case lists:member(Other, ValidCaps) of 254 true -> [{d,Edge}|EndCapAcc]; 255 false -> EndCapAcc 256 end; 257 _ -> EndCapAcc 258 end; 259 N1 when N1 > 4 -> 260 case wings_face:vertices(Rf, We2) of 261 N2 when N2 > 4 -> 262 Other = wings_vertex:other(V, E), 263 [{c,Edge,Other}|EndCapAcc]; 264 _ -> EndCapAcc 265 end; 266 _ -> EndCapAcc 267 end 268 end, [], V, We2), % vertex fold 269 case lists:sort(EndCap) of 270 [{d,E1},{c,E2,V1}] -> 271 We3 = wings_edge:dissolve_edge(E1, We2), 272 Pos = wings_vertex:pos(V1, We3), 273 #we{vp=Vtab0}=We4 = wings_collapse:collapse_edge(E2, V1, We3), 274 Vtab = array:set(V1, Pos, Vtab0), 275 We4#we{vp=Vtab}; 276 [{d,E1}|_] -> 277 #we{vp=Vtab0}=We3 = wings_edge:dissolve_edge(E1, We2), 278 RVs = wings_vertex:fold(fun(_,_,Erec,RVs0) -> 279 [wings_vertex:other(V, Erec)|RVs0] 280 end, [], V, We3), 281 Center = wings_vertex:center(RVs, We3), 282 Vtab = array:set(V, Center, Vtab0), 283 We3#we{vp=Vtab}; 284 _ -> We2 285 end 286 end, We1, NewVs0), % list foldl 287 AllVs = orddict:fetch_keys(array:sparse_to_orddict(Vtab)), 288 NewVs = ordsets:intersection(lists:sort(NewVs0), AllVs), 289 We#we{temp={Edges,NewVs,F}} 290 end, 291 St = wings_sel:map(MF, St0), 292 DF = fun(_, #we{temp=PlusMinus}) -> PlusMinus end, 293 wings_move:plus_minus(normal, DF, St). 294 295find_cap_vs(Edges0, #we{es=Etab}=We, Acc) -> 296 case gb_sets:is_empty(Edges0) of 297 true -> 298 CapVs = find_cap_vs(lists:sort(Acc), []), 299 valid_caps(CapVs, We); 300 false -> 301 {Edge,Edges} = gb_sets:take_smallest(Edges0), 302 #edge{vs=Va,ve=Vb} = array:get(Edge, Etab), 303 find_cap_vs(Edges, We, [Va,Vb|Acc]) 304 end. 305 306find_cap_vs([V,V|Vs0], CapVs) -> 307 Vs = rem_v(V, Vs0), 308 find_cap_vs(Vs, CapVs); 309find_cap_vs([V|Vs], CapVs) -> 310 find_cap_vs(Vs, [V|CapVs]); 311find_cap_vs([], CapVs) -> 312 CapVs. 313 314rem_v(V, [V|Vs]) -> 315 rem_v(V, Vs); 316rem_v(_, Vs) -> 317 Vs. 318 319valid_caps(CapVs, We) -> 320 foldl(fun(V, Acc) -> 321 Count = wings_vertex:fold(fun(_, _, _, Cnt) -> 322 Cnt+1 323 end, 0, V, We), 324 case Count rem 2 of 325 0 -> [V|Acc]; 326 _ -> Acc 327 end 328 end, [], CapVs). 329 330%% 331%% The Extrude command (for edges). 332%% 333 334extrude(Type, St0) -> 335 Dist = calc_extrude_dist(St0), 336 MF = fun(Edges, We) -> extrude_1(Edges, Dist, We) end, 337 St = wings_sel:map(MF, St0), 338 DF = fun(_, #we{temp=PlusMinus}) -> PlusMinus end, 339 wings_move:plus_minus(Type, DF, St). 340 341calc_extrude_dist(St) -> 342 Map = fun(Edges0, We) -> 343 Edges = gb_sets:to_list(Edges0), 344 min_dist_from_edges(Edges, We) 345 end, 346 Combine = fun min/2, 347 wings_sel:dfold(Map, Combine, 3.0*?DEFAULT_EXTRUDE_DIST, St) / 3.0. 348 349extrude_1(Edges, ExtrudeDist, We0) -> 350 {We1,_,New,Forbidden} = extrude_edges(Edges, ExtrudeDist, We0), 351 Ns = orig_normals(Edges, We1), 352 We = straighten(Ns, New, We1), 353 NewVs = wings_we:new_items_as_ordset(vertex, We0, We), 354 We#we{temp={Edges,NewVs,Forbidden}}. 355 356orig_normals(Es0, #we{es=Etab,vp=Vtab}) -> 357 VsVec0 = gb_sets:fold( 358 fun(E, A) -> 359 #edge{vs=Va,ve=Vb} = array:get(E, Etab), 360 Vec = e3d_vec:norm_sub(array:get(Va, Vtab), 361 array:get(Vb, Vtab)), 362 [{Va,{Vec,Vb}},{Vb,{Vec,Va}}|A] 363 end, [], Es0), 364 VsVec1 = sofs:relation(VsVec0, [{vertex,info}]), 365 VsVec2 = sofs:relation_to_family(VsVec1), 366 VsVec = sofs:to_external(VsVec2), 367 orig_normals_1(VsVec, gb_trees:from_orddict(VsVec), []). 368 369orig_normals_1([{V,[{VecA,_},{VecB,_}]}|T], VsVec, Acc) -> 370 orig_normals_1(T, VsVec, [{V,e3d_vec:cross(VecA, VecB)}|Acc]); 371orig_normals_1([{V,[{VecA,OtherV}]}|T], VsVec, Acc) -> 372 OtherRec = gb_trees:get(OtherV, VsVec), 373 case [Vec || {Vec,Vertex} <- OtherRec, Vertex =/= V] of 374 [VecB] -> 375 orig_normals_1(T, VsVec, [{V,e3d_vec:cross(VecA, VecB)}|Acc]); 376 _ -> 377 orig_normals_1(T, VsVec, Acc) 378 end; 379orig_normals_1([_|T], VsVec, Acc) -> 380 orig_normals_1(T, VsVec, Acc); 381orig_normals_1([], _, Acc) -> reverse(Acc). 382 383straighten([{V,N0}|Ns], New, #we{vp=Vtab0}=We0) -> 384 Pos = wings_vertex:pos(V, We0), 385 Vtab = wings_vertex:fold( 386 fun(_, _, R, Vt0) -> 387 OtherV = wings_vertex:other(V, R), 388 case gb_sets:is_member(OtherV, New) of 389 false -> Vt0; 390 true -> 391 OPos0 = array:get(OtherV, Vt0), 392 Vec = e3d_vec:norm_sub(Pos, OPos0), 393 case e3d_vec:dot(N0, Vec) of 394 Dot when abs(Dot) < 0.87 -> 395 Vt0; 396 Dot when Dot < 0 -> 397 N = e3d_vec:neg(N0), 398 straighten_1(Vec, N, Pos, OtherV, OPos0, Vt0); 399 _ -> 400 straighten_1(Vec, N0, Pos, OtherV, OPos0, Vt0) 401 end 402 end 403 end, Vtab0, V, We0), 404 We = We0#we{vp=Vtab}, 405 straighten(Ns, New, We); 406straighten([], _, We) -> We. 407 408straighten_1(Vec, N, {Cx,Cy,Cz}, OtherV, OPos0, Vt) -> 409 case catch e3d_mat:rotate_s_to_t(Vec, N) of 410 {'EXIT',_} -> Vt; 411 Rot -> 412 M0 = e3d_mat:translate(Cx, Cy, Cz), 413 M1 = e3d_mat:mul(M0, Rot), 414 M = e3d_mat:mul(M1, e3d_mat:translate(-Cx, -Cy, -Cz)), 415 OPos = e3d_mat:mul_point(M, OPos0), 416 array:set(OtherV, OPos, Vt) 417 end. 418 419%% 420%% Common help function for actually extruding edges. 421%% 422extrude_edges(Edges, ExtrudeDist, We) -> 423 NoForbiddenFaces = gb_sets:empty(), 424 extrude_edges(Edges, NoForbiddenFaces, ExtrudeDist, We). 425 426extrude_edges(Edges, ForbiddenFaces, ExtrudeDist, 427 #we{next_id=Wid,es=Etab,vc=Vct0}=We0) -> 428 G = digraph:new(), 429 430 %% We update the 'vc' table here to handle the case that a 431 %% a vertex's incident edge points to a completely unrelated 432 %% face (i.e. a vertex is shared by two faces that have no 433 %% common edge). 434 Vct = gb_sets:fold( 435 fun(Edge, A) -> 436 #edge{vs=Va,ve=Vb} = Rec = array:get(Edge, Etab), 437 digraph_edge(G, ForbiddenFaces, Rec), 438 array:set(Vb, Edge, array:set(Va, Edge, A)) 439 end, Vct0, Edges), 440 Vs0 = digraph:vertices(G), 441 Vs = sofs:to_external(sofs:domain(sofs:relation(Vs0))), 442 {We1,Forbidden} = 443 foldl(fun(V, A) -> 444 new_vertex(V, G, Edges, ForbiddenFaces, ExtrudeDist, A) 445 end, {We0#we{vc=Vct},[]}, Vs), 446 NewVs = wings_we:new_items_as_gbset(vertex, We0, We1), 447 We = connect(G, ExtrudeDist, Wid, We1), 448 digraph:delete(G), 449 {We,Vs,NewVs,gb_sets:from_list(Forbidden)}. 450 451new_vertex(V, G, Edges, ForbiddenFaces, ExtrudeDist, {We0,F0}=Acc) -> 452 case wings_vertex:fold(fun(E, F, R, A) -> [{E,F,R}|A] end, [], V, We0) of 453 [_,_]=Es -> 454 case filter_edges(Es, Edges, ForbiddenFaces) of 455 [] -> Acc; 456 [{Edge,_,#edge{lf=Lf,rf=Rf}}] -> 457 New = {new,V}, 458 digraph_insert(G, New, V, Lf), 459 digraph_insert(G, V, New, Lf), 460 digraph_insert(G, V, New, Rf), 461 digraph_insert(G, New, V, Rf), 462 {We0,[Edge|F0]} 463 end; 464 Es0 -> 465 Es = filter_edges(Es0, Edges, ForbiddenFaces), 466 Center = wings_vertex:pos(V, We0), 467 We = foldl(fun({Edge,_,_}, W0) -> 468 new_vertex_1(V, G, Edge, Center, ExtrudeDist, W0) 469 end, We0, Es), 470 {We,F0} 471 end. 472 473new_vertex_1(V, G, Edge, Center, ExtrudeDist, #we{es=Etab}=We0) -> 474 OtherPos = wings_vertex:other_pos(V, array:get(Edge, Etab), We0), 475 Dir = e3d_vec:norm_sub(OtherPos, Center), 476 Pos = e3d_vec:add_prod(Center, Dir, ExtrudeDist), 477 {We,NewV=NewE} = wings_edge:fast_cut(Edge, Pos, We0), 478 Rec = get_edge_rec(V, NewV, Edge, NewE, We), 479 digraph_edge(G, Rec), 480 We. 481 482get_edge_rec(Va, Vb, EdgeA, EdgeB, #we{es=Etab}) -> 483 case array:get(EdgeA, Etab) of 484 #edge{vs=Va,ve=Vb}=Rec -> Rec; 485 #edge{vs=Vb,ve=Va}=Rec -> Rec; 486 _Other -> array:get(EdgeB, Etab) 487 end. 488 489filter_edges(Es, EdgeSet, FaceSet) -> 490 foldl(fun({Edge,Face,_}=E, A) -> 491 case gb_sets:is_member(Edge, EdgeSet) orelse 492 gb_sets:is_member(Face, FaceSet) of 493 true -> A; 494 false -> [E|A] 495 end 496 end, [], Es). 497 498digraph_edge(G, #edge{lf=Lf,rf=Rf,vs=Va,ve=Vb}) -> 499 digraph_insert(G, Va, Vb, Lf), 500 digraph_insert(G, Vb, Va, Rf). 501 502digraph_edge(G, ForbiddenFaces, #edge{lf=Lf,rf=Rf,vs=Va,ve=Vb}) -> 503 case gb_sets:is_member(Lf, ForbiddenFaces) of 504 false -> digraph_insert(G, Va, Vb, Lf); 505 true -> ok 506 end, 507 case gb_sets:is_member(Rf, ForbiddenFaces) of 508 false -> digraph_insert(G, Vb, Va, Rf); 509 true -> ok 510 end. 511 512digraph_insert(G, Va0, Vb0, Face) -> 513 Va = {Va0,Face}, 514 Vb = {Vb0,Face}, 515 digraph:add_vertex(G, Va), 516 digraph:add_vertex(G, Vb), 517 digraph:add_edge(G, Va, Vb). 518 519connect(G, ExtrudeDist, Wid, We) -> 520 Cs = digraph_utils:components(G), 521 connect(G, Cs, ExtrudeDist, Wid, We, []). 522 523connect(G, [C|Cs], ExtrudeDist, Wid, #we{mirror=Mirror}=We0, Closed) -> 524 case [VF || {V,_}=VF <- C, V >= Wid] of 525 [] -> 526 case C of 527 [{_,Mirror}|_] -> 528 connect(G, Cs, ExtrudeDist, Wid, We0, Closed); 529 [{_,Face}|_] -> 530 connect(G, Cs, ExtrudeDist, Wid, We0, [Face|Closed]) 531 end; 532 [_] -> 533 extrude_problem(); 534 [Va0,Vb0] -> 535 case digraph_get_path(G, Va0, Vb0) of 536 [{_,Mirror}|_] -> 537 connect(G, Cs, ExtrudeDist, Wid, We0, Closed); 538 [{Va,Face}|Path0] -> 539 Path = [V || {V,_} <- Path0], 540 N = wings_face:normal(Face, We0), 541 We = connect_inner(Va, Path, N, Face, ExtrudeDist, We0), 542 connect(G, Cs, ExtrudeDist, Wid, We, Closed) 543 end 544 end; 545connect(_, [], ExtrudeDist, _, We0, Closed) -> 546 We = wings_extrude_face:faces(Closed, We0), 547 move_vertices(Closed, ExtrudeDist, We). 548 549digraph_get_path(G, Va, Vb) -> 550 case digraph:get_path(G, Va, Vb) of 551 false -> digraph:get_path(G, Vb, Va); 552 Path -> Path 553 end. 554 555connect_inner({new,Va}, [Va,Vb,{new,Vb}], N, Face, ExtrudeDist, We0) -> 556 [{EdgeThrough,_,_}] = wings_vertex:edge_through(Va, Vb, We0), 557 {We1,TempE} = wings_edge:fast_cut(EdgeThrough, default, We0), 558 {We2,Edge} = wings_vertex:force_connect(Vb, Va, Face, We1), 559 #we{vp=Vtab} = We2, 560 APos = array:get(Va, Vtab), 561 BPos = array:get(Vb, Vtab), 562 Vec = e3d_vec:sub(APos, BPos), 563 Pos1 = e3d_vec:add_prod(BPos, e3d_vec:cross(Vec, N), ExtrudeDist), 564 {We3,NewE} = wings_edge:fast_cut(Edge, Pos1, We2), 565 Pos2 = e3d_vec:add_prod(APos, e3d_vec:cross(Vec, N), ExtrudeDist), 566 We4 = wings_edge:dissolve_edge(TempE, We3), 567 {We,_} = wings_edge:fast_cut(NewE, Pos2, We4), 568 wings_we_util:validate(We), 569 We; 570connect_inner({new,V}, [V|[B,C,_|_]=Next], N, Face, ExtrudeDist, We0) -> 571 {We1,Current} = connect_one_inner(V, V, B, C, N, Face, ExtrudeDist, We0), 572 #we{vp=Vtab} = We2 = connect_inner(Current, Next, N, Face, ExtrudeDist, We1), 573 Edge = wings_vertex:fold( 574 fun(E, _, R, A) -> 575 case wings_vertex:other(V, R) of 576 Current -> E; 577 _ -> A 578 end 579 end, none, V, We2), 580 VPos = array:get(V, Vtab), 581 BPos = array:get(B, Vtab), 582 Vec = e3d_vec:sub(VPos, BPos), 583 Pos = e3d_vec:add_prod(VPos, e3d_vec:cross(Vec, N), ExtrudeDist), 584 {We,_} = wings_edge:fast_cut(Edge, Pos, We2), 585 We; 586connect_inner({new,_}, [A|[B,C]], _, Face, _, We0) -> 587 {We1,Edge} = wings_vertex:force_connect(C, A, Face, We0), 588 #we{vp=Vtab} = We1, 589 APos = array:get(A, Vtab), 590 BPos = array:get(B, Vtab), 591 CPos = array:get(C, Vtab), 592 Pos = e3d_vec:add(APos, e3d_vec:sub(CPos, BPos)), 593 {We,_} = wings_edge:fast_cut(Edge, Pos, We1), 594 We; 595connect_inner(C, [B|[A,{new,_}]], N, Face, ExtrudeDist, We0) -> 596 {We1,Edge} = wings_vertex:force_connect(A, C, Face, We0), 597 #we{vp=Vtab} = We1, 598 APos = array:get(A, Vtab), 599 BPos = array:get(B, Vtab), 600 Vec = e3d_vec:sub(BPos, APos), 601 Pos = e3d_vec:add_prod(APos, e3d_vec:cross(Vec, N), ExtrudeDist), 602 {We,_} = wings_edge:fast_cut(Edge, Pos, We1), 603 We; 604connect_inner(Current0, [A|[B,C,_|_]=Next], N, Face, ExtrudeDist, We0) -> 605 {We,Current} = connect_one_inner(Current0, A, B, C, N, Face, ExtrudeDist, We0), 606 connect_inner(Current, Next, N, Face, ExtrudeDist, We); 607connect_inner(Current, [_|[_,_]=Next], N, Face, ExtrudeDist, We) -> 608 connect_inner(Current, Next, N, Face, ExtrudeDist, We); 609connect_inner(Current, [_,Last], _, Face, _, We0) -> 610 {We,_} = wings_vertex:force_connect(Last, Current, Face, We0), 611 We. 612 613connect_one_inner(Current, A, B, C, N, Face, ExtrudeDist, We0) -> 614 {We1,Edge} = wings_vertex:force_connect(B, Current, Face, We0), 615 #we{vp=Vtab} = We1, 616 Pos = new_vertex_pos(A, B, C, N, ExtrudeDist, Vtab), 617 wings_edge:fast_cut(Edge, Pos, We1). 618 619move_vertices([Face|Fs], ExtrudeDist, #we{vp=Vtab0}=We0) -> 620 N = wings_face:normal(Face, We0), 621 Vs = wings_face:vertices_ccw(Face, We0), 622 Vtab = move_vertices(Vs, Vs, N, ExtrudeDist, Vtab0, Vtab0), 623 We = We0#we{vp=Vtab}, 624 move_vertices(Fs, ExtrudeDist, We); 625move_vertices([], _, We) -> We. 626 627move_vertices([Va|[Vb,Vc|_]=Vs], First, N, ExtrudeDist, OldVtab, Vtab0) -> 628 Pos = new_vertex_pos(Va, Vb, Vc, N, ExtrudeDist, OldVtab), 629 Vtab = array:set(Vb, wings_util:share(Pos), Vtab0), 630 move_vertices(Vs, First, N, ExtrudeDist, OldVtab, Vtab); 631move_vertices([Va,Vb], [Vc,Vd|_], N, ExtrudeDist, OldVtab, Vtab) -> 632 move_vertices([Va,Vb,Vc,Vd], [], N, ExtrudeDist, OldVtab, Vtab); 633move_vertices([_,_], [], _, _, _, Vtab) -> Vtab. 634 635new_vertex_pos(A, B, C, N, ExtrudeDist, Vtab) -> 636 APos = array:get(A, Vtab), 637 BPos = array:get(B, Vtab), 638 CPos = array:get(C, Vtab), 639 VecA0 = e3d_vec:norm_sub(APos, BPos), 640 VecB0 = e3d_vec:norm_sub(BPos, CPos), 641 VecA = e3d_vec:norm(e3d_vec:cross(VecA0, N)), 642 VecB = e3d_vec:norm(e3d_vec:cross(VecB0, N)), 643 Vec = average(VecA, VecB), 644 e3d_vec:add_prod(BPos, Vec, ExtrudeDist). 645 646average(Na, Nb) -> 647 N = e3d_vec:norm(e3d_vec:add(Na, Nb)), 648 case e3d_vec:dot(N, Na) of 649 Dot when abs(Dot) < 1.0E-6 -> 650 e3d_vec:add(Na, Nb); 651 Dot -> 652 e3d_vec:divide(N, Dot) 653 end. 654 655scale_tv(Tv, ExtrudeDist) -> 656 S = 1.0 / ExtrudeDist, 657 scale_tv_1(Tv, S, []). 658 659scale_tv_1([{Vec,Vs}|T], S, Acc) -> 660 scale_tv_1(T, S, [{e3d_vec:mul(Vec, S),Vs}|Acc]); 661scale_tv_1([], _, Acc) -> Acc. 662 663-spec min_dist_from_edges(Edges, #we{}) -> float() when 664 Edges :: nonempty_list(edge_num()). 665 666min_dist_from_edges(Edges0, We) -> 667 Vs = wings_vertex:from_edges(Edges0, We), 668 Edges = wings_edge:from_vs(Vs, We), 669 min_dist_from_edges_1(Edges, We, infinite). 670 671min_dist_from_edges_1([E|Es], #we{es=Etab,vp=Vtab}=We, D0) -> 672 #edge{vs=Va,ve=Vb} = array:get(E, Etab), 673 D = e3d_vec:dist(array:get(Va, Vtab), array:get(Vb, Vtab)), 674 min_dist_from_edges_1(Es, We, min(D0, D)); 675min_dist_from_edges_1([], _, D) -> D. 676