diff --git a/Fltk/classificationEditor.cpp b/Fltk/classificationEditor.cpp
index edaeb678221b4b49cf7f545bbae57fd6feac8814..2665eee9ae433dee98d1c2908f234ef0e4c5cf51 100644
--- a/Fltk/classificationEditor.cpp
+++ b/Fltk/classificationEditor.cpp
@@ -16,49 +16,10 @@
 #include "GmshMessage.h"
 #include "MLine.h"
 #include "meshGFaceDelaunayInsertion.h"
-#include "meshGFaceOptimize.h"
 #include "discreteEdge.h"
 #include "discreteFace.h"
 
-edge_angle::edge_angle(MVertex *_v1, MVertex *_v2, MElement *t1, MElement *t2)
-  : v1(_v1), v2(_v2)
-{
-  if(!t2) angle = 0;
-  else{
-    double c1[3];
-    double c2[3];
-    double c3[3];
-    {
-      MVertex *p1 = t1->getVertex(0);
-      MVertex *p2 = t1->getVertex(1);
-      MVertex *p3 = t1->getVertex(2);
-      double a[3] = {p1->x() - p2->x(), p1->y() - p2->y(), p1->z() - p2->z()};
-      double b[3] = {p1->x() - p3->x(), p1->y() - p3->y(), p1->z() - p3->z()};
-      c1[2] = a[0] * b[1] - a[1] * b[0];
-      c1[1] = -a[0] * b[2] + a[2] * b[0];
-      c1[0] = a[1] * b[2] - a[2] * b[1];
-    }
-    {
-      MVertex *p1 = t2->getVertex(0);
-      MVertex *p2 = t2->getVertex(1);
-      MVertex *p3 = t2->getVertex(2);
-      double a[3] = {p1->x() - p2->x(), p1->y() - p2->y(), p1->z() - p2->z()};
-      double b[3] = {p1->x() - p3->x(), p1->y() - p3->y(), p1->z() - p3->z()};
-      c2[2] = a[0] * b[1] - a[1] * b[0];
-      c2[1] = -a[0] * b[2] + a[2] * b[0];
-      c2[0] = a[1] * b[2] - a[2] * b[1];
-    }
-    norme(c1);
-    norme(c2);
-    prodve(c1, c2, c3);
-    double cosa; prosca(c1, c2, &cosa);
-    double sina = norme(c3);
-    angle = atan2(sina, cosa);
-  }
-}
-
-struct compareMLinePtr 
-{
+struct compareMLinePtr {
   bool operator () (MLine *l1, MLine *l2) const
   {
     static Less_Edge le;
@@ -66,23 +27,6 @@ struct compareMLinePtr
   }
 };
 
-static void buildListOfEdgeAngle(e2t_cont adj, std::vector<edge_angle> &edges_detected,
-                                 std::vector<edge_angle> &edges_lonly)
-{
-  e2t_cont::iterator it = adj.begin();
-  for(; it != adj.end(); ++it){
-    if(it->second.second)
-      edges_detected.push_back(edge_angle(it->first.getVertex(0), 
-                                          it->first.getVertex(1), 
-                                          it->second.first, it->second.second));
-    else 
-      edges_lonly.push_back(edge_angle(it->first.getVertex(0),
-                                       it->first.getVertex(1), 
-                                       it->second.first, it->second.second));
-  }
-  std::sort(edges_detected.begin(), edges_detected.end());
-}
-
 static void recurClassify(MTri3 *t, GFace *gf,
                           std::map<MLine*, GEdge*, compareMLinePtr> &lines,
                           std::map<MTriangle*, GFace*> &reverse)
@@ -105,7 +49,7 @@ static void recurClassify(MTri3 *t, GFace *gf,
 }
 
 static GEdge *getNewModelEdge(GFace *gf1, GFace *gf2, 
-                              std::map<std::pair<int, int>, GEdge* > &newEdges)
+                              std::map<std::pair<int, int>, GEdge*> &newEdges)
 {
   int t1 = gf1 ? gf1->tag() : -1;
   int t2 = gf2 ? gf2->tag() : -1;
diff --git a/Fltk/classificationEditor.h b/Fltk/classificationEditor.h
index b7153dded18f4b6a460d3738a0db8b2bbe2f4a28..b226b14d720dccd2ae4f5df27230970e17312e6e 100644
--- a/Fltk/classificationEditor.h
+++ b/Fltk/classificationEditor.h
@@ -13,6 +13,7 @@
 #include <FL/Fl_Value_Input.H>
 #include "GModel.h"
 #include "MElement.h"
+#include "meshGFaceOptimize.h"
 
 #define CLASS_BUTTON_SELECT_ELEMENTS       0
 #define CLASS_BUTTON_SELECT_ALL_ELEMENTS   1
@@ -28,17 +29,6 @@
 
 #define CLASS_VALUE_ANGLE                  0
 
-class edge_angle {
- public :
-  MVertex *v1, *v2;
-  double angle;
-  edge_angle(MVertex *_v1, MVertex *_v2, MElement *t1, MElement *t2);
-  bool operator < (const edge_angle &other) const
-  {
-    return other.angle < angle;
-  }  
-};
-
 class classificationEditor {
  public:
   std::vector<MTriangle*> elements;
diff --git a/Mesh/meshGFaceOptimize.cpp b/Mesh/meshGFaceOptimize.cpp
index 0cefd31171b45f0409d1cd31ad23557046fc9b4f..319d09c79817060af4d2f34f5e8e7067de604b2f 100644
--- a/Mesh/meshGFaceOptimize.cpp
+++ b/Mesh/meshGFaceOptimize.cpp
@@ -16,6 +16,43 @@
 #include "GmshMessage.h"
 #include "Generator.h"
 
+edge_angle::edge_angle(MVertex *_v1, MVertex *_v2, MElement *t1, MElement *t2)
+  : v1(_v1), v2(_v2)
+{
+  if(!t2) angle = 0;
+  else{
+    double c1[3];
+    double c2[3];
+    double c3[3];
+    {
+      MVertex *p1 = t1->getVertex(0);
+      MVertex *p2 = t1->getVertex(1);
+      MVertex *p3 = t1->getVertex(2);
+      double a[3] = {p1->x() - p2->x(), p1->y() - p2->y(), p1->z() - p2->z()};
+      double b[3] = {p1->x() - p3->x(), p1->y() - p3->y(), p1->z() - p3->z()};
+      c1[2] = a[0] * b[1] - a[1] * b[0];
+      c1[1] = -a[0] * b[2] + a[2] * b[0];
+      c1[0] = a[1] * b[2] - a[2] * b[1];
+    }
+    {
+      MVertex *p1 = t2->getVertex(0);
+      MVertex *p2 = t2->getVertex(1);
+      MVertex *p3 = t2->getVertex(2);
+      double a[3] = {p1->x() - p2->x(), p1->y() - p2->y(), p1->z() - p2->z()};
+      double b[3] = {p1->x() - p3->x(), p1->y() - p3->y(), p1->z() - p3->z()};
+      c2[2] = a[0] * b[1] - a[1] * b[0];
+      c2[1] = -a[0] * b[2] + a[2] * b[0];
+      c2[0] = a[1] * b[2] - a[2] * b[1];
+    }
+    norme(c1);
+    norme(c2);
+    prodve(c1, c2, c3);
+    double cosa; prosca(c1, c2, &cosa);
+    double sina = norme(c3);
+    angle = atan2(sina, cosa);
+  }
+}
+
 static void setLcsInit(MTriangle *t, std::map<MVertex*, double> &vSizes)
 {
   for(int i = 0; i < 3; i++){
@@ -194,6 +231,23 @@ void buildEdgeToTriangle(std::vector<MTriangle*> &tris, e2t_cont &adj)
   buildEdgeToElement(tris, adj);
 }
 
+void buildListOfEdgeAngle(e2t_cont adj, std::vector<edge_angle> &edges_detected,
+                          std::vector<edge_angle> &edges_lonly)
+{
+  e2t_cont::iterator it = adj.begin();
+  for(; it != adj.end(); ++it){
+    if(it->second.second)
+      edges_detected.push_back(edge_angle(it->first.getVertex(0), 
+                                          it->first.getVertex(1), 
+                                          it->second.first, it->second.second));
+    else 
+      edges_lonly.push_back(edge_angle(it->first.getVertex(0),
+                                       it->first.getVertex(1), 
+                                       it->second.first, it->second.second));
+  }
+  std::sort(edges_detected.begin(), edges_detected.end());
+}
+
 void parametricCoordinates(MElement *t, GFace *gf, double u[4], double v[4])
 {
   for(int j = 0; j < t->getNumVertices(); j++){
diff --git a/Mesh/meshGFaceOptimize.h b/Mesh/meshGFaceOptimize.h
index 92e91df7be1fb5d88876a7f2816a8cba4818f139..e6b8e7538a5ee6a848da596ad29d24abc3688f92 100644
--- a/Mesh/meshGFaceOptimize.h
+++ b/Mesh/meshGFaceOptimize.h
@@ -16,11 +16,24 @@
 class GFace;
 class MVertex;
 
+class edge_angle {
+ public :
+  MVertex *v1, *v2;
+  double angle;
+  edge_angle(MVertex *_v1, MVertex *_v2, MElement *t1, MElement *t2);
+  bool operator < (const edge_angle &other) const
+  {
+    return other.angle < angle;
+  }  
+};
+
 typedef std::map<MVertex*, std::vector<MElement*> > v2t_cont;
 typedef std::map<MEdge, std::pair<MElement*, MElement*>, Less_Edge> e2t_cont;
 
 void buildVertexToTriangle(std::vector<MTriangle*> &, v2t_cont &adj);
 void buildEdgeToTriangle(std::vector<MTriangle*> &, e2t_cont &adj);
+void buildListOfEdgeAngle(e2t_cont adj, std::vector<edge_angle> &edges_detected,
+                          std::vector<edge_angle> &edges_lonly);
 void laplaceSmoothing(GFace *gf);
 void edgeSwappingLawson(GFace *gf);
 
diff --git a/Plugin/CMakeLists.txt b/Plugin/CMakeLists.txt
index 12b32aa25e5ffb3566f0101f8c89447d46ad19b8..55637ba2fb456ef4e06a34c45ca07810fe5b45a0 100644
--- a/Plugin/CMakeLists.txt
+++ b/Plugin/CMakeLists.txt
@@ -23,7 +23,7 @@ set(SRC
   Probe.cpp
   HarmonicToTime.cpp ModulusPhase.cpp
   HomologyComputation.cpp
-  Distance.cpp
+  Distance.cpp ExtractEdges.cpp
 )
 
 file(GLOB HDR RELATIVE ${CMAKE_SOURCE_DIR}/Plugin *.h) 
diff --git a/Plugin/ExtractEdges.cpp b/Plugin/ExtractEdges.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f3fade5443ed4ee06d581438021a7e405b4bc9ca
--- /dev/null
+++ b/Plugin/ExtractEdges.cpp
@@ -0,0 +1,91 @@
+// Gmsh - Copyright (C) 1997-2010 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#include "GModel.h"
+#include "meshGFaceOptimize.h"
+#include "ExtractEdges.h"
+
+StringXNumber ExtractEdgesOptions_Number[] = {
+  {GMSH_FULLRC, "Angle", NULL, 40.},
+  {GMSH_FULLRC, "IncludeBoundary", NULL, 1.},
+};
+
+extern "C"
+{
+  GMSH_Plugin *GMSH_RegisterExtractEdgesPlugin()
+  {
+    return new GMSH_ExtractEdgesPlugin();
+  }
+}
+
+std::string GMSH_ExtractEdgesPlugin::getHelp() const
+{
+  return "Plugin(ExtractEdges) extracts sharp edges "
+    "from a triangular mesh.\n\n"
+    "Plugin(ExtractEdges) creates one new view.";
+}
+
+int GMSH_ExtractEdgesPlugin::getNbOptions() const
+{
+  return sizeof(ExtractEdgesOptions_Number) / sizeof(StringXNumber);
+}
+
+StringXNumber *GMSH_ExtractEdgesPlugin::getOption(int iopt)
+{
+  return &ExtractEdgesOptions_Number[iopt];
+}
+
+static void add_edge(edge_angle &ea, PViewDataList *data)
+{
+  data->SL.push_back(ea.v1->x());
+  data->SL.push_back(ea.v2->x());
+  data->SL.push_back(ea.v1->y());
+  data->SL.push_back(ea.v2->y());
+  data->SL.push_back(ea.v1->z());
+  data->SL.push_back(ea.v2->z());
+  data->SL.push_back(1.);
+  data->SL.push_back(1.);
+  data->NbSL++;
+}
+
+PView *GMSH_ExtractEdgesPlugin::execute(PView *v)
+{
+  std::vector<MTriangle*> elements;
+  for(GModel::fiter it = GModel::current()->firstFace(); 
+      it != GModel::current()->lastFace(); ++it)
+    elements.insert(elements.end(), (*it)->triangles.begin(), 
+                    (*it)->triangles.end());
+  
+  if(elements.empty()){
+    Msg::Error("No triangles in mesh to extract edges from");
+    return 0;
+  }
+
+  PView *v2 = new PView();
+  PViewDataList *data2 = getDataList(v2);
+
+  e2t_cont adj;
+  buildEdgeToTriangle(elements, adj);
+  std::vector<edge_angle> edges_detected, edges_lonly;
+  buildListOfEdgeAngle(adj, edges_detected, edges_lonly);
+
+  double threshold = ExtractEdgesOptions_Number[0].def / 180. * M_PI;
+  for(unsigned int i = 0; i < edges_detected.size(); i++){
+    if(edges_detected[i].angle <= threshold) break;
+    add_edge(edges_detected[i], data2);
+  } 
+
+  if(ExtractEdgesOptions_Number[1].def){
+    for(unsigned int i = 0; i < edges_lonly.size(); i++){
+      add_edge(edges_lonly[i], data2);
+    } 
+  }
+
+  data2->setName("ExtractEdges");
+  data2->setFileName("ExtractEdges.pos");
+  data2->finalize();
+
+  return v2;
+}
diff --git a/Plugin/ExtractEdges.h b/Plugin/ExtractEdges.h
new file mode 100644
index 0000000000000000000000000000000000000000..795ac0ed44304af5baa76311f603271226b72816
--- /dev/null
+++ b/Plugin/ExtractEdges.h
@@ -0,0 +1,31 @@
+// Gmsh - Copyright (C) 1997-2010 C. Geuzaine, J.-F. Remacle
+//
+// See the LICENSE.txt file for license information. Please report all
+// bugs and problems to <gmsh@geuz.org>.
+
+#ifndef _EXTRACT_EDGES_H_
+#define _EXTRACT_EDGES_H_
+
+#include "Plugin.h"
+
+extern "C"
+{
+  GMSH_Plugin *GMSH_RegisterExtractEdgesPlugin();
+}
+
+class GMSH_ExtractEdgesPlugin : public GMSH_PostPlugin
+{
+ public:
+  GMSH_ExtractEdgesPlugin(){}
+  std::string getName() const { return "ExtractEdges"; }
+  std::string getShortHelp() const
+  {
+    return "Extract sharp edges from triangular mesh";
+  }
+  std::string getHelp() const;
+  int getNbOptions() const;
+  StringXNumber* getOption(int iopt);  
+  PView *execute(PView *);
+};
+
+#endif
diff --git a/Plugin/PluginManager.cpp b/Plugin/PluginManager.cpp
index 2848acc40ad9907882fa2096ad1f20a139ae1320..592f1eb8ef8536364d93e8ebd72e5e1a2afb3bea 100644
--- a/Plugin/PluginManager.cpp
+++ b/Plugin/PluginManager.cpp
@@ -44,6 +44,7 @@
 #include "GSHHS.h"
 #include "HomologyComputation.h"
 #include "Distance.h"
+#include "ExtractEdges.h"
 
 // for testing purposes only :-)
 #undef HAVE_DLOPEN
@@ -217,6 +218,8 @@ void PluginManager::registerDefaultPlugins()
 #endif
     allPlugins.insert(std::pair<std::string, GMSH_Plugin*>
                       ("Distance", GMSH_RegisterDistancePlugin()));
+    allPlugins.insert(std::pair<std::string, GMSH_Plugin*>
+                      ("ExtractEdges", GMSH_RegisterExtractEdgesPlugin()));
   }
 
 #if defined(HAVE_FLTK)