diff --git a/Geo/CMakeLists.txt b/Geo/CMakeLists.txt
index 2c56e9b75c9551f9b2e5d9bbc867b66b94357e0b..eb9fba44b4e5a79d3354e42788c6aaf3def7dd4f 100644
--- a/Geo/CMakeLists.txt
+++ b/Geo/CMakeLists.txt
@@ -11,6 +11,7 @@ set(SRC
     GEdgeLoop.cpp GEdgeCompound.cpp GFaceCompound.cpp
   GRegionCompound.cpp GRbf.cpp
     gmshVertex.cpp gmshEdge.cpp gmshFace.cpp gmshRegion.cpp
+    gmshEdgeDiscretize.cpp
    gmshSurface.cpp
     OCCVertex.cpp OCCEdge.cpp OCCFace.cpp OCCRegion.cpp
     discreteEdge.cpp discreteFace.cpp discreteRegion.cpp
diff --git a/Geo/GEdge.h b/Geo/GEdge.h
index 0e5b5cfeb41f58f7c6fdaf43e9c994aedc3fa8aa..cf7b3cbfe9680c41b7a767480a07621da561db44 100644
--- a/Geo/GEdge.h
+++ b/Geo/GEdge.h
@@ -209,6 +209,7 @@ class GEdge : public GEntity {
   void addLine(MLine *line){ lines.push_back(line); }
 
   bool computeDistanceFromMeshToGeometry (double &d2, double &dmax);
+  virtual void discretize(double tol, std::vector<SPoint3> &dpts, std::vector<double> &ts) {Msg::Fatal("not implemented");}
 };
 
 #endif
diff --git a/Geo/gmshEdge.h b/Geo/gmshEdge.h
index f54bcfb4f8f110c6edb439493d34d6cdec33f2aa..3c92afd14a1a54a14261ffb5ab218caae7ceb880 100644
--- a/Geo/gmshEdge.h
+++ b/Geo/gmshEdge.h
@@ -29,6 +29,7 @@ class gmshEdge : public GEdge {
   virtual void resetMeshAttributes();
   virtual SPoint2 reparamOnFace(const GFace *face, double epar, int dir) const;
   virtual void writeGEO(FILE *fp);
+  void discretize(double tol, std::vector<SPoint3> &dpts, std::vector<double> &ts);
 };
 
 #endif
diff --git a/Geo/gmshEdgeDiscretize.cpp b/Geo/gmshEdgeDiscretize.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..440ebe4f7c3ef7dc58b9ae85265b048c2a265720
--- /dev/null
+++ b/Geo/gmshEdgeDiscretize.cpp
@@ -0,0 +1,195 @@
+#include <cstdio>
+#include <cmath>
+#include <vector>
+#include "SPoint3.h"
+#include "SVector3.h"
+#include "GEdge.h"
+#include "gmshEdge.h"
+#include "Geo.h"
+
+class discreteList {
+  std::vector<std::pair<SPoint3, double> > _pts;
+  std::vector<int> _next;
+  public:
+  int insertPoint(int pos, const SPoint3 &pt, double t) {
+    _pts.push_back(std::make_pair(pt, t));
+    _next.push_back(_next[pos + 1]);
+    _next[pos + 1] = _pts.size() - 1;
+    return _pts.size() - 1;
+  }
+  void sort(std::vector<SPoint3> &spts, std::vector<double> &ts) {
+    spts.clear();
+    spts.reserve(_pts.size());
+    ts.clear();
+    ts.reserve(_pts.size());
+    for (int p = _next[0]; p != -1; p = _next[p + 1]) {
+      spts.push_back(_pts[p].first);
+      ts.push_back(_pts[p].second);
+    }
+  }
+  discreteList() {
+    _next.push_back(-1);
+  }
+};
+
+static void decasteljau(double tol, discreteList &discrete, int pos, const SPoint3 &p0, const SPoint3 &p1, const SPoint3 &p2, const SPoint3 &p3, double t0, double t3)
+{
+  SVector3 d30 = p3 - p0;
+  SVector3 d13 = p1 - p3;
+  SVector3 d23 = p2 - p3;
+  SVector3 d130 = crossprod(d13, d30);
+  SVector3 d230 = crossprod(d23, d30);
+  double d = std::max(dot(d130, d130), dot(d230, d230));
+  double l2 = dot(d30, d30);
+
+  if(d < tol * tol * l2) {
+    return;
+  }
+
+  SPoint3 p01((p0 + p1) * 0.5);
+  SPoint3 p12((p1 + p2) * 0.5);
+  SPoint3 p23((p2 + p3) * 0.5);
+  SPoint3 p012((p01 + p12) * 0.5);
+  SPoint3 p123((p12 + p23) * 0.5);
+  SPoint3 p0123((p012 + p123) * 0.5);
+  double t0123 = 0.5 * (t0 + t3);
+  int newpos = discrete.insertPoint(pos, p0123, t0123);
+
+  decasteljau(tol, discrete, pos, p0, p01, p012, p0123, t0, t0123);
+  decasteljau(tol, discrete, newpos, p0123, p123, p23, p3, t0123, t3);
+}
+
+static int discretizeBezier(double tol, discreteList &discrete, int pos, const SPoint3 pt[4], double t0, double t3, bool insertFirstPoint)
+{
+  if (insertFirstPoint)
+    pos = discrete.insertPoint(pos, pt[0], t0);
+  int newp = discrete.insertPoint(pos, pt[3], t3);
+  decasteljau(tol, discrete, pos, pt[0], pt[1], pt[2], pt[3], t0, t3);
+  return newp;
+}
+
+static int discretizeBSpline(double tol, discreteList &discrete, int pos, const SPoint3 pt[4], double t0, double t3, bool insertFirstPoint)
+{
+  SPoint3 bpt[4] = {
+    SPoint3((pt[0] + 4 * pt[1]  + pt[2]) * (1./6.)),
+    SPoint3((2 * pt[1] + pt[2]) * (1./3.)),
+    SPoint3((pt[1] + 2 * pt[2]) * (1./3.)),
+    SPoint3((pt[1] + 4 * pt[2] + pt[3]) * (1./6.))
+  };
+  return discretizeBezier(tol, discrete, pos, bpt, t0, t3, insertFirstPoint);
+}
+
+static int discretizeCatmullRom(double tol, discreteList &discrete, int pos, const SPoint3 pt[4], double t0, double t3, bool insertFirstPoint)
+{
+  SPoint3 bpt[4] = {
+    pt[1],
+    SPoint3(( 6 * pt[1] + pt[2] - pt[0]) * (1./6.)),
+    SPoint3(( 6 * pt[2] - pt[3] + pt[1]) * (1./6.)),
+    pt[2]
+  };
+  return discretizeBezier(tol, discrete, pos, bpt, t0, t3, insertFirstPoint);
+}
+
+static inline SPoint3 curveGetPoint(Curve *c, int i)
+{
+  Vertex *v;
+  List_Read(c->Control_Points, i , &v);
+  return SPoint3(v->Pos.X, v->Pos.Y, v->Pos.Z);
+}
+
+static void discretizeCurve(Curve *c, double tol, std::vector<SPoint3> &pts, std::vector<double> &ts)
+{
+  discreteList discrete;
+  switch(c->Typ) {
+    case MSH_SEGM_LINE :
+      {
+        int NPt = List_Nbr(c->Control_Points);
+        pts.resize(NPt);
+        ts.resize(NPt);
+        for (int i = 0; i < NPt; ++i) {
+          pts[i]= curveGetPoint(c, i);
+          ts[i] = i / (double) (NPt - 1);
+        }
+        return;
+      }
+    case MSH_SEGM_BEZIER :
+      {
+        int back = -1;
+        int NbCurves = (List_Nbr(c->Control_Points) - 1) / 3;
+        for (int iCurve = 0; iCurve < NbCurves; ++iCurve) {
+          double t1 = (iCurve) / (double)(NbCurves);
+          double t2 = (iCurve+1) / (double)(NbCurves);
+          SPoint3 pt[4];
+          for(int i = 0; i < 4; i++) {
+            pt[i] = curveGetPoint(c, iCurve * 3 + i);
+          }
+          back = discretizeBezier(tol, discrete, back, pt, t1, t2, iCurve == 0);
+        }
+        break;
+      }
+    case MSH_SEGM_BSPLN:
+      {
+        int back = -1;
+        bool periodic = (c->end == c->beg);
+        int NbControlPoints = List_Nbr(c->Control_Points);
+        int NbCurves = NbControlPoints + (periodic ? -1 : 1);
+        SPoint3 pt[4];
+        for (int iCurve = 0; iCurve < NbCurves; ++iCurve) {
+          double t1 = (iCurve) / (double)(NbCurves);
+          double t2 = (iCurve+1) / (double)(NbCurves);
+          for(int i = 0; i < 4; i++) {
+            int k;
+            if (periodic) {
+              k = (iCurve - 1 + i) % (NbControlPoints - 1);
+              if (k < 0)
+                k += NbControlPoints - 1;
+            }
+            else {
+              k = std::max(0, std::min(iCurve - 2 + i, NbControlPoints -1));
+            }
+            pt[i] = curveGetPoint(c, k);
+          }
+          back = discretizeBSpline(tol, discrete, back, pt, t1, t2, iCurve == 0);
+        }
+        break;
+      }
+    case MSH_SEGM_SPLN:
+      {
+        int NbCurves = List_Nbr(c->Control_Points) - 1;
+        SPoint3 pt[4];
+        int back = -1;
+        for (int iCurve = 0; iCurve < NbCurves; ++iCurve) {
+          double t1 = (iCurve) / (double)(NbCurves);
+          double t2 = (iCurve+1) / (double)(NbCurves);
+          pt[1] = curveGetPoint(c, iCurve);
+          pt[2] = curveGetPoint(c, iCurve + 1);
+          if(iCurve == 0) {
+            if(c->beg == c->end)
+              pt[0] = curveGetPoint(c, NbCurves - 1);
+            else
+              pt[0] = SPoint3(pt[1] * 2 - pt[2]);
+          }
+          else
+            pt[0] = curveGetPoint(c, iCurve - 1);
+          if(iCurve == NbCurves - 1) {
+            if(c->beg == c->end)
+              pt[3] = curveGetPoint(c, 1);
+            else
+              pt[3] = SPoint3(2 * pt[2] - pt[1]);
+          }
+          else
+            pt[3] = curveGetPoint(c, iCurve + 2);
+          back = discretizeCatmullRom(tol, discrete, back, pt, t1, t2, iCurve == 0);
+        }
+        break;
+      }
+    default :
+      Msg::Fatal("not implemented");
+  }
+  discrete.sort(pts, ts);
+}
+
+void gmshEdge::discretize(double tol, std::vector<SPoint3> &dpts, std::vector<double> &ts)
+{
+  discretizeCurve(c, tol, dpts, ts);
+}
diff --git a/wrappers/gmshpy/gmshGeo.i b/wrappers/gmshpy/gmshGeo.i
index da667b39972f6398a4d9cad475cac75eb6d23704..49d6ad1e9f250088c7445ab95226b27d789266e3 100644
--- a/wrappers/gmshpy/gmshGeo.i
+++ b/wrappers/gmshpy/gmshGeo.i
@@ -58,6 +58,7 @@ namespace std {
   %template(DoubleVector) std::vector<double, std::allocator<double> >;
   %template(DoubleVectorVector) std::vector<std::vector<double, std::allocator<double> > >;
   %template(StringVector) std::vector<std::string, std::allocator<std::string> >;
+  %template(SPoint3Vector) std::vector<SPoint3, std::allocator<SPoint3> >;
 }
 
 %include "GmshConfig.h"
@@ -72,6 +73,8 @@ namespace std {
 %include "GPoint.h"  
 %include "GEntity.h"
 %include "GVertex.h"
+%apply std::vector<double> &OUTPUT{std::vector<double> &ts}
+%apply std::vector<SPoint3> &OUTPUT{std::vector<SPoint3> &dpts}
 %include "GEdge.h"
 %include "GFace.h"
 %include "GRegion.h"