Advertisement
ansimionescu

wings_tesselation.erl

Jun 15th, 2012
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Erlang 9.75 KB | None | 0 0
  1. %%
  2. %%  wings_tesselation.erl --
  3. %%
  4. %%     Tesselation/subdivision commands.
  5. %%
  6. %%  Copyright (c) 2001-2009 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_tesselation).
  15. -export([submenu/0,command/2]).
  16. -export([triangulate/1,triangulate/2,quadrangulate/1,quadrangulate/2]).
  17. -export([is_good_triangulation/5]).
  18.  
  19. -include_lib("wings.hrl").
  20. -include_lib("e3d.hrl").
  21. -import(lists, [reverse/1]).
  22.  
  23. submenu() ->
  24.     [{?STR(submenu,1,"Triangulate"),triangulate},
  25.      {?STR(submenu,2,"Quadrangulate"),quadrangulate}].
  26.  
  27. command(triangulate, St) ->
  28.     Action = fun triangulate/2,
  29.     {St1,Sel} = wings_sel:mapfold(fun(Fs, We, A) ->
  30.                       do_faces(Action, Fs, We, A) end,
  31.                   [], St),
  32.     {save_state,St1#st{sel=reverse(Sel)}};
  33. command(quadrangulate, St) ->
  34.     Action = fun quadrangulate/2,
  35.     {St1,Sel} = wings_sel:mapfold(fun(Fs, We, A) ->
  36.                       do_faces(Action, Fs, We, A) end,
  37.                   [], St),
  38.     {save_state,St1#st{sel=reverse(Sel)}}.
  39.  
  40. triangulate(#we{fs=Ftab}=We) ->
  41.     triangulate(gb_sets:from_ordset(gb_trees:keys(Ftab)), We).
  42.  
  43. triangulate(Faces, We) when is_list(Faces) ->
  44.     triangulate(gb_sets:from_list(Faces), We);
  45. triangulate(Faces, We) ->
  46.     tri_faces(Faces, We).
  47.  
  48. quadrangulate(#we{fs=Ftab}=We) ->
  49.     quadrangulate(gb_trees:keys(Ftab), We).
  50.  
  51. quadrangulate(Faces, We) when is_list(Faces) ->
  52.     tess_faces(Faces, We);
  53. quadrangulate(Faces, We) ->
  54.     quadrangulate(gb_sets:to_list(Faces), We).
  55.  
  56. %% is_good_triangulation(Normal, PointA, PointB, PointC, PointD) -> true|false
  57. %%  The points PointA through PointD are assumed to be the vertices of
  58. %%  quadrilateral in counterclockwise order, and Normal should be the
  59. %%  averaged normal for the quad.
  60. %%
  61. %%  This function will determine whether a triangulation created by
  62. %%  joining PointA to PointC is a good triangulation (thus creating
  63. %%  the two triangles PointA-PointB-PointC and PointA-PointC-PointD).
  64. %%  This function returns 'true' if none of the two triangles is degenerated
  65. %%  and the diagonal PointA-PointC is inside the original quad (if the
  66. %%  quad is concave, one of the "diagonals" will be inside the quad).
  67. %%
  68. %%  This function returns 'false' if the PointA-PointC triangulation is
  69. %%  bad. Except for pathoglogical quads (e.g. non-planar or warped), the other
  70. %%  triangulation using the PointB-PointD triangulation should be OK.
  71. %%
  72. is_good_triangulation({Nx,Ny,Nz}, {Ax,Ay,Az}, {Bx,By,Bz}, {Cx,Cy,Cz}, {Dx,Dy,Dz})
  73.   when is_float(Ax), is_float(Ay), is_float(Az) ->
  74.     %% Construct the normals for the two triangles by calculating the
  75.     %% cross product of two edges in the correct order:
  76.     %%
  77.     %%    NormalTri1 = (PointC-PointA) x (PointA-PointB)
  78.     %%    NormalTri2 = (PointD-PointA) x (PointA-PointC)
  79.     %%
  80.     %% The normals should point in about the same direction as the
  81.     %% normal for the quad. We certainly expect the angle between a
  82.     %% triangle normal and the quad normal to be less than 90
  83.     %% degrees. That can be verified by taking the dot product:
  84.     %%
  85.     %%    Dot1 = QuadNormal . NormalTri1
  86.     %%    Dot2 = QuadNormal . NormalTri2
  87.     %%
  88.     %% Both dot products should be greater than zero. A zero dot product either
  89.     %% means that the triangle normal was not defined (a degenerate triangle) or
  90.     %% that the angle is exactly 90 degrees. A negative dot product means that
  91.     %% the angle is greater than 90 degress, which implies that the PointA-PointC
  92.     %% line is outside the quad.
  93.     %%
  94.     CAx = Cx-Ax, CAy = Cy-Ay, CAz = Cz-Az,
  95.     ABx = Ax-Bx, ABy = Ay-By, ABz = Az-Bz,
  96.     DAx = Dx-Ax, DAy = Dy-Ay, DAz = Dz-Az,
  97.     D1 = Nx*(CAy*ABz-CAz*ABy) + Ny*(CAz*ABx-CAx*ABz) + Nz*(CAx*ABy-CAy*ABx),
  98.     D2 = Nx*(DAz*CAy-DAy*CAz) + Ny*(DAx*CAz-DAz*CAx) + Nz*(DAy*CAx-DAx*CAy),
  99.     is_good_triangulation_1(D1, D2).
  100.  
  101. %%%
  102. %%% Internal functions.
  103. %%%
  104.  
  105. is_good_triangulation_1(D1, D2) when D1 > 0.0, D2 > 0.0 -> true;
  106. is_good_triangulation_1(_, _) -> false.
  107.  
  108. do_faces(Action, Faces, #we{id=Id}=We0, Acc) ->
  109.     We = Action(Faces, We0),
  110.     Sel = gb_sets:union(wings_we:new_items_as_gbset(face, We0, We), Faces),
  111.     {We,[{Id,Sel}|Acc]}.
  112.  
  113. tess_faces([], We) -> We;
  114. tess_faces([F|T], We) -> tess_faces(T, doface(F, We)).
  115.  
  116. doface(Face, We) ->
  117.     Vs = wings_face:vertices_ccw(Face, We),
  118.     case length(Vs) of
  119.     Len when Len =< 3 -> We;
  120.     Len when Len =< 4 -> We;
  121.     Len -> doface_1(Face,Len, Vs, We, true)
  122.     end.
  123.  
  124. tri_faces(Fs0,We0) ->
  125.     tri_faces([],Fs0,gb_sets:empty(), We0).  
  126.  
  127. tri_faces([], Fs0, TriV0, We0) ->
  128.     case gb_sets:is_empty(Fs0) of
  129.     true -> We0;
  130.     false ->
  131.         {Face, Fs1} = gb_sets:take_smallest(Fs0),
  132.         {Pref, Fs, TriV, We} = triface(Face,Fs1,TriV0,We0),
  133.         tri_faces(Pref, Fs,TriV,We)
  134.     end;
  135. tri_faces([Face|R],Fs0,TriV0,We0) ->
  136.     {Pref, Fs, TriV,We} = triface(Face,Fs0,TriV0,We0),
  137.     tri_faces(Pref ++ R,Fs,TriV,We).
  138.  
  139. triface(Face, Fs, TriV,We) ->
  140.     Vs = wings_face:vertices_ccw(Face, We),
  141.     case length(Vs) of
  142.     3 -> {[], Fs, TriV, We};
  143.     4 ->
  144.         triangulate_quad(Face, Vs, TriV, Fs, We);
  145.     Len -> {[], Fs, TriV, doface_1(Face,Len, Vs, We, false)}
  146.     end.
  147.  
  148. %%  Triangulates a quad, tries to make the triangulation so nice
  149. %%  patterns emerges, or otherwise along the shortest diagonal, Then
  150. %%  checking that normals for the triangles are consistent with the
  151. %%  normal for the quad. Falls back to the general triangulator if
  152. %%  normals are inconsistent (= concave or otherwise strange quad).
  153.  
  154. triangulate_quad(F, Vs, TriV0, FsSet0, #we{vp=Vtab}=We0) ->
  155.     VsPos = [array:get(V, Vtab) || V <- Vs],
  156.     try
  157.     {V1,V2,TriV,We} = triangulate_quad_1(VsPos, Vs, F, TriV0, We0),
  158.     {Fs1, FsSet1} = get_pref_faces(V1,FsSet0,We),
  159.     {Fs2, FsSet} = get_pref_faces(V2,FsSet1,We),
  160.     {Fs1++Fs2,FsSet,TriV,We}
  161.     catch throw:_Problematic ->
  162.         {[],FsSet0,TriV0, doface_1(F,4, Vs, We0, false)};
  163.     Type:Err ->
  164.         io:format("~p:~p: ~p ~p ~p~n",
  165.               [?MODULE,?LINE, Type, Err, erlang:get_stacktrace()])
  166.     end.
  167.  
  168. get_pref_faces(V,Fs0,We) ->
  169.     wings_vertex:fold(fun(_,F,_,{Acc,FsSet}) ->
  170.                   case gb_sets:is_member(F,FsSet) of
  171.                   true -> {[F|Acc], gb_sets:delete(F,FsSet)};
  172.                   false -> {Acc,FsSet}
  173.                   end
  174.               end, {[],Fs0}, V, We).
  175.  
  176. triangulate_quad_1(VsPos=[A,B,C,D], Vi=[Ai,Bi,Ci,Di], F, TriV, We) ->
  177.     N = e3d_vec:normal(VsPos),
  178.     ACgood = gb_sets:is_member(Ai,TriV) orelse
  179.     gb_sets:is_member(Ci,TriV),
  180.     BDgood = gb_sets:is_member(Bi,TriV) orelse
  181.     gb_sets:is_member(Di,TriV),
  182.     [V1,V2] =
  183.     if ACgood, (not BDgood) ->
  184.         assert_quad2tris(N,A,B,C,D,F),
  185.         [Ai,Ci];
  186.        BDgood, (not ACgood) ->
  187.         assert_quad2tris(N,B,C,D,A,F),
  188.         [Bi,Di];
  189.        true ->
  190.         select_newedge(VsPos,Vi,N,F)
  191.     end,
  192.     {NewWe,_NewFace} = wings_vertex:force_connect(V1,V2,F,We),
  193.     {V1,V2,gb_sets:add(V2,gb_sets:add(V1,TriV)), NewWe}.
  194.  
  195. select_newedge(_L = [A,B,C,D],[Ai,Bi,Ci,Di],N,F) ->
  196.     AC = e3d_vec:dist(A, C),
  197.     BD = e3d_vec:dist(B, D),
  198.     Epsilon = 0.15,  %% 1/6 diffs Is rougly equal
  199.     case AC < BD of
  200.     true when ((BD-AC) / BD)  > Epsilon ->
  201.         assert_quad2tris(N,A,B,C,D,F),
  202.         [Ai,Ci];
  203.     _ ->
  204.         assert_quad2tris(N,B,C,D,A,F),
  205.         [Bi,Di]
  206.     end.
  207.  
  208. %% Good enough triangles
  209. -define(TRI_AREA, 0.70).  
  210. %% This allows pretty big area diff, but avoid areas close to 0.
  211. assert_quad2tris(N,A,B,C,D,F) ->
  212.     try
  213.     case is_good_triangulation(N, A, B, C, D) of
  214.         true ->
  215.         T1 = e3d_vec:area(A,B,C),
  216.         T2 = e3d_vec:area(C,D,A),
  217.         case (abs(T1-T2) / (T1+T2)) < 0.80 of
  218.             true -> ok;
  219.             _ ->
  220.             throw(F)
  221.         end;
  222.         false ->
  223.         throw(F)
  224.     end
  225.     catch error:_ ->
  226.         throw(F)
  227.     end.
  228.  
  229. doface_1(Face,Len,Vs,#we{vp=Vtab}=We, Q) ->
  230.     FaceVs = lists:seq(0, Len-1),
  231.     Vcoords = [array:get(V, Vtab) || V <- Vs],
  232.     E3dface = #e3d_face{vs=FaceVs},
  233.     T3dfaces = case Q of
  234.            true -> e3d_mesh:quadrangulate_face(E3dface, Vcoords);
  235.            false -> e3d_mesh:triangulate_face(E3dface, Vcoords)
  236.            end,
  237.     VsTuple = list_to_tuple(Vs),
  238.     Tfaces = [renumber(FVs, VsTuple) || #e3d_face{vs=FVs} <- T3dfaces],
  239.     Bord = bedges(Vs),
  240.     Diags = diags(Tfaces, Bord),
  241.     connect_diags(Diags, [{Vs,Face}], Q, We).
  242.  
  243. renumber(L, Vtab) ->
  244.     renumber(L, Vtab, []).
  245. renumber([V|Vs], Vtab, Acc) ->
  246.     renumber(Vs, Vtab, [element(V+1, Vtab)|Acc]);
  247. renumber([], _, Acc) -> reverse(Acc).
  248.  
  249. %% This simple code only works because we assume that each
  250. %% vertex can appear only once in a face.
  251. connect_diags([], _Faces, _Q, We) -> We;
  252. connect_diags([{A,B}|T], Faces, Q, We0) ->
  253.     case find_face(A,B,Faces) of
  254.     none -> %% Hmm
  255.         connect_diags(T, Faces, Q, We0);
  256.     Face ->
  257.         {We,NewFace} = wings_vertex:force_connect(A,B,Face,We0),
  258.         Vs = wings_face:vertices_ccw(NewFace, We),
  259.         connect_diags(T,[{Vs,NewFace}|Faces],Q,We)
  260.     end.
  261.  
  262. find_face(_,_,[]) -> none;
  263. find_face(A,B,[{Vs, Face}|Fs]) ->
  264.     case lists:member(A,Vs) andalso lists:member(B,Vs) of
  265.     true ->  Face;
  266.     false -> find_face(A,B,Fs)
  267.     end.
  268.  
  269. %% Return GbSet of {A,B} where AB is an edge in face F
  270. bedges(F) -> bedges(F, F, []).
  271.  
  272. bedges([A], [B|_], S) -> gb_sets:from_list([{A,B}|S]);
  273. bedges([A|[B|_]=T], F, S) when A < B ->
  274.     bedges(T, F, [{A,B}|S]);
  275. bedges([A|[B|_]=T], F, S) ->
  276.     bedges(T, F, [{B,A}|S]).
  277.  
  278. diags(Fl, Bord) -> diags1(Fl, {Bord, []}).
  279.  
  280. diags1([], {_, S}) -> reverse(S);
  281. diags1([Vs|T], Acc) -> diags1(T, diagsf(Vs, Vs, Acc)).
  282.  
  283. diagsf([A], [First|_], Acc) ->
  284.     trydiag(A, First, Acc);
  285. diagsf([A|[B|_]=T], Vs, Acc) ->
  286.     diagsf(T,Vs,trydiag(A, B, Acc)).
  287.  
  288. trydiag(A, B, Acc) when A > B ->
  289.     %% only want one representative of diag
  290.     Acc;
  291. trydiag(A, B, Old={Bord, S}) ->
  292.     E = {A,B},
  293.     case gb_sets:is_member(E, Bord) of
  294.     true -> Old;
  295.     false -> {gb_sets:add(E,Bord),[E|S]}
  296.     end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement