<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -19,7 +19,8 @@
 -export([select_all/1]).
 
 -include(&quot;wings.hrl&quot;).
--import(lists, [map/2,foldl/3,reverse/1,keymember/3,keysearch/3]).
+-import(lists, [map/2,foldl/3,reverse/1,keymember/3,keysearch/3,usort/1]).
+-import(erlang, [max/2]).
 
 menu(St) -&gt;
     [{?__(1,&quot;Deselect&quot;),deselect,?__(2,&quot;Clear the selection&quot;)},
@@ -618,7 +619,7 @@ similar(#st{selmode=vertex}=St) -&gt;
 				  [make_vertex_template(SelI, We) ||
 				      SelI &lt;- gb_sets:to_list(Sel0)] ++ A
 			  end, [], St),
-    Templates = ordsets:from_list(Seed),
+    Templates = consolidate_templates(Seed),
     wings_sel:make(
       fun(V, W) -&gt;
 	      match_templates(make_vertex_template(V, W), Templates)
@@ -628,7 +629,7 @@ similar(#st{selmode=edge}=St) -&gt;
 				  [make_edge_template(SelI, We) ||
 				      SelI &lt;- gb_sets:to_list(Sel0)] ++ A
 			  end, [], St),
-    Templates = ordsets:from_list(Seed),
+    Templates = consolidate_templates(Seed),
     wings_sel:make(
       fun(Edge, W) -&gt;
 	      match_templates(make_edge_template(Edge, W), Templates)
@@ -638,7 +639,7 @@ similar(#st{selmode=face}=St) -&gt;
 				  [make_face_template(SelI, We) ||
 				      SelI &lt;- gb_sets:to_list(Sel0)] ++ A
 			  end, [], St),
-    Templates = ordsets:from_list(Seed),
+    Templates = consolidate_templates(Seed),
     wings_sel:make(
       fun(Face, WeI) -&gt;
 	      match_templates(make_face_template(Face, WeI), Templates)
@@ -652,6 +653,21 @@ similar(#st{selmode=body}=St) -&gt;
     Template = ordsets:from_list(Template0),
     wings_sel:make(fun(_, We) -&gt; match_body(Template, We) end, body, St).
 
+consolidate_templates(L) -&gt;
+    case usort(L) of
+	[] -&gt; [];
+	[H|T] -&gt; consolidate_templates_1(H, T)
+    end.
+
+consolidate_templates_1(Templ0, [Templ|T]) -&gt;
+    case match_template(Templ0, Templ) of
+	true -&gt;
+	    consolidate_templates_1(Templ0, T);
+	false-&gt;
+	    [Templ0|consolidate_templates_1(Templ, T)]
+    end;
+consolidate_templates_1(Templ, []) -&gt; [Templ].
+
 match_body(Template, #we{vp=Vtab,es=Etab,fs=Ftab}) -&gt;
     Sizes = {wings_util:array_entries(Vtab),
 	     wings_util:array_entries(Etab),
@@ -670,25 +686,15 @@ match_templates(F, [Template|Ts]) -&gt;
 match_templates(_, []) -&gt; false.
 
 match_template({Len,Ad,As}, {Len,Bd,Bs}) -&gt;
-    case rel_compare(Ad, Bd, 1.0E-5) of
-	true -&gt; rel_compare(As, Bs, 1.0E-5);
-	false -&gt; false
-    end;
+    compare(Ad, Bd) andalso compare(As, Bs);
 match_template(_, _) -&gt; false.
 
-make_face_template(Face, #we{vp=Vtab}=We) -&gt;
-    Vs = wings_face:fold(
-	   fun(V, _, _, Acc0) -&gt;
-		   [V|Acc0]
-	   end, [], Face, We),
-    {DotSum,SqSum} = face_dots_and_sqlens(Vs, Vtab),
-    {length(Vs),DotSum,SqSum}.
-
-face_dots_and_sqlens(Vs, Vtab) -&gt;
-    Vpos = [array:get(P, Vtab) || P &lt;- Vs],
-    face_dots_and_sqlens_1(Vpos).
+make_face_template(Face, We) -&gt;
+    VsPos = wings_face:vertex_positions(Face, We),
+    {DotSum,SqSum} = face_dots_and_sqlens(VsPos),
+    {length(VsPos),DotSum,SqSum}.
 
-face_dots_and_sqlens_1([Va,Vb|_]=Vpos) -&gt;
+face_dots_and_sqlens([Va,Vb|_]=Vpos) -&gt;
     D = e3d_vec:sub(Va, Vb),
     face_dots_and_sqlens_2(D, Vpos, Vpos, 0, 0).
 
@@ -738,12 +744,15 @@ vertex_dots_and_sqlens(Vecs, [VecB|_], Dot, Sq) -&gt;
     vertex_dots_and_sqlens(Vecs++[VecB], [], Dot, Sq);
 vertex_dots_and_sqlens(_Other, _More, Dot, Sq) -&gt; {Dot,Sq}.
 
-rel_compare(A, B, Tresh) when abs(A) &lt; Tresh -&gt;
-    abs(B) &lt; Tresh;
-rel_compare(A, B, Tresh) when abs(A) &gt; abs(B) -&gt;
-    abs(A-B)/abs(A) &lt; Tresh;
-rel_compare(A, B, Tresh) -&gt;
-    abs(A-B)/abs(B) &lt; Tresh.
+-define(EPSILON, 1.0E-5).
+compare(A, B) -&gt;
+    %% Comparison with a relative tolerance for large
+    %% values and an absolute tolerance for small values
+    %% as described by Christer Ericson in
+    %% http://realtimecollisiondetection.net/blog/?p=89
+    %% and in his book &quot;Real-Time Collision Detection&quot;.
+    %%
+    abs(A-B) =&lt; ?EPSILON*max(1.0, max(abs(A), abs(B))).
 
 %%
 %% Select Random.</diff>
      <filename>src/wings_sel_cmd.erl</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>e8e192ab7715e1ebe968be7ed594623479f3c955</id>
    </parent>
  </parents>
  <author>
    <name>Bjorn Gustavsson</name>
    <email>bjorng@users.sourceforge.net</email>
  </author>
  <url>http://github.com/bjorng/wings/commit/348f6aaa05a16cdee1621ca50e69bbea061de130</url>
  <id>348f6aaa05a16cdee1621ca50e69bbea061de130</id>
  <committed-date>2009-06-24T01:15:43-07:00</committed-date>
  <authored-date>2009-06-24T00:55:59-07:00</authored-date>
  <message>Speed up Select|Similar when the seed selection contains similar elements

After having calculated the templates for all elements
in the selection, we do an ordsets:from_list/1 to eliminate
duplicates. The problem is that two templates that are
ALMOST equal will both be kept in the list.

To get rid of a template that is almost equal to another
template, first sort the list of templates, then go through
the list and match templates against each other using
match_template/2. If they match (i.e. if they are similar),
only keep the first template. That will leave us with a
minimum number of templates that should match the same
elements.

Also change the comparison function to the one described
by Christer Ericson in Real-Time Collision Detection, and
simplify the creation of face templates by using
wings_face:vertex_positions/2.

NOTE: Select|Similar is now faster if the initial selection
contains elements that are similar to each other.
(Thanks to Ran13.) [bjorng]</message>
  <tree>c9ca5e43f67bc624e9532a442cfc13c3cbe11e75</tree>
  <committer>
    <name>Bjorn Gustavsson</name>
    <email>bjorng@users.sourceforge.net</email>
  </committer>
</commit>
