diff --git a/compositemodel/include/GoTools/compositemodel/HedgeSurface.h b/compositemodel/include/GoTools/compositemodel/HedgeSurface.h
new file mode 100644
index 00000000..70d43682
--- /dev/null
+++ b/compositemodel/include/GoTools/compositemodel/HedgeSurface.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#ifndef _HEDGESURFACE_H
+#define _HEDGESURFACE_H
+
+#include "GoTools/compositemodel/ftSurface.h"
+#include "GoTools/geometry/ClassType.h"
+//#include "GoTools/compositemodel/RevEngRegion.h"
+
+namespace Go {
+
+class RevEngRegion;
+  class CurveOnSurface;
+  
+  /// Additional information to the ClassType of a ParamSurface to distinguish
+  /// between different rotational surfaces.  LINEARSWEPT_SURF is currently not
+  /// active while ROTATIONALSWEPT_SURF is not implemented
+  enum
+    {
+     SURF_TYPE_UNDEF, LINEARSWEPT_SURF, ROTATIONALSWEPT_SURF
+    };
+  
+  /**  HedgeSurface - A topological surface associated with a RevEngRegion. 
+       The class provides extra information and functionality compared to ftSurface, 
+       mainly related to information about the RevEngRegion.
+   *
+   */
+
+class HedgeSurface : public ftSurface
+{
+public:
+  
+  /// Constructor
+  HedgeSurface();
+
+  /// Constructor given geometry surface and associated region
+  HedgeSurface(shared_ptr<ParamSurface> sf, RevEngRegion *region);
+
+  /// Constructor given geometry surface and a number of associated regions. Not active
+  HedgeSurface(shared_ptr<ParamSurface> sf, std::vector<RevEngRegion*>& region);
+
+  /// Destructor
+  ~HedgeSurface();
+
+  /// Add information required to define a linear swept spline surface
+  void setLinearSweepInfo(shared_ptr<SplineCurve> profile,
+			  Point startpt, Point endpt)
+  {
+    surf_code_ = LINEARSWEPT_SURF;
+    profile_ = profile;
+    sweep1_ = startpt;
+    sweep2_ = endpt;
+  }
+  
+  /// Add information required to define a rotational swept spline surface. Not implemented
+  void setRotationalSweepInfo(shared_ptr<SplineCurve> profile,
+			      Point location, Point axis)
+  {
+    surf_code_ = ROTATIONALSWEPT_SURF;
+    profile = profile_;
+    sweep1_ = location;
+    sweep2_ = axis;
+  }
+
+  /// Dimension of geometry space (should be 3)
+  int dimension()
+  {
+    return surface()->dimension();
+  }
+
+  /// Number of points in associated region(s)
+  int numPoints();
+
+  /// Class type of geometry surface and possible swept surface code
+  ClassType instanceType(int& code);
+
+  /// Enquire if the surfae is a plane
+  bool isPlane()
+  {
+    int code;
+    return (instanceType(code) == Class_Plane);
+  }
+
+  /// Enquire if the surfae is a cylinder
+  bool isCylinder()
+  {
+    int code;
+    return (instanceType(code) == Class_Cylinder);
+  }
+
+  /// Enquire if the surfae is a sphere
+  bool isSphere()
+  {
+    int code;
+    return (instanceType(code) == Class_Sphere);
+  }
+
+  /// Enquire if the surfae is a torus
+  bool isTorus()
+  {
+    int code;
+    return (instanceType(code) == Class_Torus);
+  }
+
+  /// Enquire if the surfae is a cone
+  bool isCone()
+  {
+    int code;
+    return (instanceType(code) == Class_Cone);
+  }
+
+  /// Enquire if the surfae is a freeform surface
+  bool isSpline()
+  {
+    int code;
+    return (instanceType(code) == Class_SplineSurface);
+  }
+
+  /// Fetch associated region(s)
+  std::vector<RevEngRegion*> getRegions()
+  {
+    return regions_;
+  }
+
+  /// Number of associated regions. Expected to be one
+  int numRegions()
+  {
+    return (int)regions_.size();
+  }
+
+  /// Fetch specified region
+  RevEngRegion* getRegion(int ix)
+  {
+    if (ix < 0 || ix >= (int)regions_.size())
+      return 0;
+    else
+      return regions_[ix];
+  }
+
+  /// Add region to collection of associated regions
+  void addRegion(RevEngRegion* reg);
+  
+  /// Remove region from collection of associated regions
+  bool removeRegion(RevEngRegion* reg);
+
+  /// Bounding box containing associated regions points
+  BoundingBox regionsBox()
+  {
+    return bbox_;
+  }
+
+  /// Check if the geometry surfaces if entity and other is of the same type and has
+  /// roughly the same characteristica. Is it a potential to merge surfaces?
+  bool isCompatible(HedgeSurface* other, double angtol, double approx_tol,
+		    ClassType& type, double& score);
+
+  /// Ensure that the associated geometry surface is bounded (e.g. not an unlimited plane)
+  void ensureSurfaceBounded();
+
+  /// Bound unbounded primary surfaces 
+  void limitSurf(double diag = -1.0);
+
+  /// Make bounded surface when trimming edges are missing. Bound the associated region points
+  /// in the parameter domain of the surface and transfer this information to this surface
+  bool trimWithPoints(double aeps);
+
+  /// Store current stage of hedge surface to file
+  void store(std::ostream& os) const;
+
+  /// Read hedge surface stage from file
+  void read(std::istream& is);
+    
+private:
+  /// Region(s) to which this surface is associated (only one)
+  std::vector<RevEngRegion*> regions_;
+
+  /// Bounding box of the associated region
+  BoundingBox bbox_;
+
+  /// Additional class type information to specify swept spline surfaces
+  int surf_code_;
+
+  /// The profile curve in a swept surface
+  shared_ptr<SplineCurve> profile_;
+
+  /// Sweep direction
+  Point sweep1_;
+  Point sweep2_;
+
+  bool updateSurfaceWithAxis(Point axis[3], int ix, double tol, double angtol);
+    
+  bool updatePlaneWithAxis(Point axis[3], int ix, double tol, double angtol);
+    
+  bool updateCylinderWithAxis(Point axis[3], int ix, double tol, double angtol);
+    
+  bool checkAccuracyAndUpdate(shared_ptr<ParamSurface> surf, double tol,
+			      double angtol);
+
+  bool hasBaseSf();
+
+  // Enquire if it is safe to intersect this surface and surf. Tangential intersections are
+  // unstable and can produce infinite loops
+  bool isTangential(HedgeSurface* surf);
+
+  void doTrim(std::vector<shared_ptr<CurveOnSurface> >& int_cvs,
+	      shared_ptr<BoundedSurface>& bdsf,
+	      double tol,
+	      std::vector<shared_ptr<HedgeSurface> >& added_sfs);
+
+};
+}
+
+#endif // _HEDGESURFACE_H
diff --git a/compositemodel/include/GoTools/compositemodel/ImplicitApprox.h b/compositemodel/include/GoTools/compositemodel/ImplicitApprox.h
new file mode 100644
index 00000000..3bca493f
--- /dev/null
+++ b/compositemodel/include/GoTools/compositemodel/ImplicitApprox.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#ifndef _IMPLICITAPPROX_H_
+#define _IMPLICITAPPROX_H_
+
+#include "GoTools/implicitization/BernsteinTetrahedralPoly.h"
+#include "GoTools/utils/BaryCoordSystem.h"
+#include "GoTools/utils/Point.h"
+
+namespace Go
+{
+  class RevEngPoint;
+
+  /**
+     ImlicitApprox - Interface class to Go::implicitization for reverse engineering
+     purposes. Used only to approximate a point cloud with a plane (degree one). Higher
+     degree approximations are unstable for noisy point clouds
+   *
+   */
+  class ImplicitApprox
+  {
+  public:
+    /// Constructor
+    ImplicitApprox();
+
+    /// Destructor
+    ~ImplicitApprox();
+
+    /// Approximate a group of RevEngPoints with a surface of degree degree.
+    /// Recommended only for degree one
+    void approx(std::vector<RevEngPoint*> points, int degree);
+
+     /// Approximate several groups of RevEngPoints with a surface of degree degree.
+    /// Recommended only for degree one
+   void approx(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+			     std::vector<RevEngPoint*>::iterator> >& points,
+		int degree);
+
+    /// Approximate a group of points with a surface of degree degree.
+    /// Recommended only for degree one
+    void approxPoints(std::vector<Point> points, int degree);
+
+    /// Project initial point and direction onto implicit surface to get a point
+    /// in the surface and corresponding surface normal
+    bool projectPoint(Point point, Point dir,
+		      Point& projpos, Point& normal);
+
+    /// \param pt Input point
+    /// \param val Implicit distance between the point pt and the surface
+    /// \param grad Gradient at point pt
+    void evaluate(Point& pt, double& val, Point& grad);
+    
+    
+  private:
+    /// Degree of implicit surface
+    int degree_;
+
+    /// Bernstein polynomials on a tetrahedron
+    BernsteinTetrahedralPoly implicit_;
+
+    /// implicit_ differentiated
+    BernsteinTetrahedralPoly deriv1_, deriv2_, deriv3_, deriv4_;
+
+    // Encapsulates a barycentric coordinate system.
+    BaryCoordSystem3D bc_;
+    double sigma_min_;
+    double eps_;
+    
+    double estimateDist(RevEngPoint* pt);
+
+    void visualize(std::vector<RevEngPoint*> points, std::ostream& os);
+
+    void visualize(std::vector<Point> points, Point& dir, std::ostream& os);
+
+    void polynomialSurf(std::vector<Point>& pos_and_der, int degree,
+			std::vector<double>& coefs);
+
+    void polynomialSurfAccuracy(std::vector<Point>& pos_and_der, 
+				int degree, std::vector<double>& coefs,
+				double& maxfield, double& avfield,
+				double& maxdist, double& avdist,
+				int& ndiv, double& maxang,
+				double& avang);
+  };
+}
+
+#endif // _IMPLICITAPPROX_H_
diff --git a/compositemodel/include/GoTools/compositemodel/RevEng.h b/compositemodel/include/GoTools/compositemodel/RevEng.h
new file mode 100644
index 00000000..9c471044
--- /dev/null
+++ b/compositemodel/include/GoTools/compositemodel/RevEng.h
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#ifndef _REVENG_H
+#define _REVENG_H
+
+#include "GoTools/compositemodel/ftPointSet.h"
+#include "GoTools/compositemodel/SurfaceModel.h"
+#include "GoTools/compositemodel/RevEngRegion.h"
+#include "GoTools/utils/Point.h"
+#include "GoTools/utils/BoundingBox.h"
+#include <string.h>
+
+namespace Go
+{
+  class RevEngPoint;
+  class HedgeSurface;
+  class RevEngEdge;
+
+  /// Elementary surface types to be recognized
+  enum
+  {
+   PLANE, CYLINDER, SPHERE, CONE, TORUS
+  };
+
+  /// Characterization of model surface. Note that the default choice is ROUGH,
+  /// and that other choices are currently not supported
+  enum
+  {
+   SMOOTH=0, MEDIUM_ROUGH, ROUGH
+  };
+
+  /// Collection of surface information used in computation of global parameters
+  struct SurfaceProperties
+  {
+    int sfix_;
+    ClassType type_;
+    ClassType prev_type_;
+    Point dir_, loc_;
+    double rad1_, rad2_;
+    int num_points_;
+    int surfflag_;
+
+    SurfaceProperties(int ix, ClassType type, int num, int surfflag, Point& dir, 
+		      Point& loc, ClassType prev_type=Class_Unknown, double rad1=-1,
+		      double rad2=-1)
+    {
+      sfix_ = ix;
+      type_ = type;
+      num_points_ = num;
+      surfflag_ = surfflag;
+      dir_ = dir;
+      loc_ = loc;
+      prev_type_ = prev_type;
+      rad1_ = rad1;
+      rad2_ = rad2;
+    }
+  };
+
+  /// Information computed in adaptToMainAxis. Specifies model axes including
+  /// axis location and number of points supporting the information
+  struct AxisInfo
+  {
+    Point axis_;
+    std::vector<std::pair<Point,int> > plane_loc_;
+    std::vector<std::pair<Point,int> > rotational_loc_;
+
+    AxisInfo(Point& axis)
+    {
+      axis_ = axis;
+    }
+
+    void addPlaneLocation(Point& loc, int num)
+    {
+      plane_loc_.push_back(std::make_pair(loc,num));
+    }
+    
+    void addRotationalLocation(Point& loc, int num)
+    {
+      rotational_loc_.push_back(std::make_pair(loc,num));
+    }
+  };
+    
+  /** RevEng -  Reverse engineering engine. Workflow funcationality and 
+      storage of data.
+   * 
+   */
+  
+  class RevEng
+  {
+  public:
+    /// Default constructor
+    RevEng();
+    
+    /// Constructor
+    /// \param tri_sf Initial triangulated surface
+    RevEng(shared_ptr<ftPointSet> tri_sf);
+
+    /// Destructor
+    ~RevEng();
+
+    /// Enhance points (triangle vertices) with estimated surface normal and
+    /// curvature information
+    void enhancePoints();
+
+    /// Classify points with respect to estimated curvature information
+    /// Possible classifications: edge point (high curvature), peak (negative mean
+    /// curvature and positive Gauss), ridge (negative mean, zero Gauss),
+    /// sridge (negative mean and Gauss), non (zero mean, positive Gauss, not expected),
+    /// flat (zero mean and Gauss), minsurf (zero mean, negative Gauss),
+    /// pit (positive mean and Gauss), valley (positive mean, zero Gauss)
+    /// and svalley (positive mean, negative Gauss)
+    void classifyPoints();
+
+    /// Group connected points with the same classification into regions
+    void segmentIntoRegions();
+
+    /// Create first surfaces from large regions, simultanously dismissing
+    /// points that do not belong to the region. Possible surfaces: planes,
+    /// cylinders and cones
+    void initialSurfaces();
+
+    /// Add adjacent regions to regions with surfaces if appropriate
+    void growSurfaces();
+
+    /// Identify coordinate axes corresponding to the current model and
+    /// update surfaces as appropriate (if their surface normal/axis almost
+    /// corresponds to a coordinate axis and the approximation error stays
+    /// limited)
+    void updateAxesAndSurfaces();
+
+    /// Compute intersection edges between defined neighbouring and almost
+    /// neighbouring surfaces provided that these surfaces
+    void firstEdges();
+
+    /// Second attempt to create surfaces. Some more regions might have grown to a
+    /// size that allows surface creation. Context information from adjacent surfaces
+    /// are used to guide the surface recognition. In addition to planes, cylinders and
+    /// cones are spheres and torii created. Context information is used to split
+    /// large composite regions. Additional edges may be created.
+    void surfaceCreation(int pass=1);
+
+    /// Identify common surface axes/normals and define a local coordinate system.
+    /// Update surfaces accordingly
+    void updateRegionsAndSurfaces(int& ix, std::vector<RevEngRegion*>& grown_regions,
+				  std::vector<HedgeSurface*>& adj_surfs);
+
+    /// Second round in identifying main axes in the model. In addition to axis
+    /// direction is location registered. Surfaces are updated accordingly.
+    void adaptToMainAxis();
+
+    /// Third attempt to create surfaces. Small regions with similar characteristica
+    /// are merged and subject to surface recognition. The required region size is
+    /// reduced. Possible surfaces: planes, cylinders and cones. Additional edges
+    /// may be created.
+    void smallRegionSurfaces();
+
+    /// Create edge blends of type cylinder and torus associated with identified
+    /// edges. Blend surfaces with almost similar radius are made consistent.
+    void manageBlends1();
+
+    /// Bound blend surface along the edge. Identify corner blends of type
+    /// torus and 4-sided free form surface. Identify and extract trimming edges
+    /// from blend surface and define associated trimming edges for adjacent surfaces
+    void manageBlends2();
+
+    /// Compute missing trimming edges, between surfaces and towards regions without
+    /// surfaces. Extract relevant part of trimming edges, add missing edges as appropriate
+    /// and combine edges into trimming loops. Define bounded surfaces and associated faces.
+    void trimSurfaces();
+
+    /// Perform topology analysis and create surface model that can be exported in a g22 file
+    shared_ptr<SurfaceModel> createModel();
+
+    /// Compute approximation tolerance based on model properties
+    void setApproxTolerance();
+
+    /// Set approximation tolerance from application
+    void setApproxTol(double eps)
+    {
+      approx_tol_= eps;
+    }
+    
+    /// Enquire defined approximation tolerance
+    double getApproxTol()
+    {
+      return approx_tol_;
+    }
+
+    /// Enquire edge classification type. Current: always curvature
+    int getEdgeClassificationType()
+    {
+      return edge_class_type_;
+    }
+
+    /// Enquire parameter for edge classification. Relates to estimated curvature
+    /// radius in point and average length of triangle edges
+    double getCfac()
+    {
+      return cfac_;
+    }
+
+    /// Set parameter for edge classification
+    void setCfac(double cfac)
+    {
+      cfac_ = cfac;
+    }
+
+    /// Enquire point classification type. Current: always curvature
+    int getClassificationType()
+    {
+      return classification_type_;
+    }
+
+    /// Enquire limit for when mean curvature is considered to be zero
+    double getMeanCurvatureZero()
+    {
+      return zero_H_;
+    }
+
+    /// Set limit for when mean curvature is considered to be zero
+    void setMeanCurvatureZero(double zero_H)
+    {
+      zero_H_ = zero_H;
+    }
+
+    
+    /// Enquire limit for when Gauss curvature is considered to be zero
+    double getGaussCurvatureZero()
+    {
+      return zero_K_;
+    }
+
+    /// Set limit for when Gauss curvature is considered to be zero
+    void setGaussCurvatureZero(double zero_K)
+    {
+      zero_K_ = zero_K;
+    }
+
+    /// Enquire preference for primary surfaces relative to free form surfaces. Always
+    /// primary, free form is currently disabled exept for some corner blends
+    int getElementaryPreferLevel()
+    {
+      return prefer_elementary_;
+    }
+
+    /// Enquire model characterization involved in the definition of the approximation tolerance.
+    /// Currently: always ROUGH
+    int getModelCharacterization()
+    {
+      return model_character_;
+    }
+
+    /// The main axes of the model is computed by updateAxesAndSurfaces(). Prelimenary axes
+    /// my be set prior to this function, but they should not be altered afterwards
+   void setMainAxis(Point mainaxis[3])
+    {
+      mainaxis_[0] = mainaxis[0];
+      mainaxis_[1] = mainaxis[1];
+      mainaxis_[2] = mainaxis[2];
+    }
+
+    /// Enquire local coordinate axes
+    void getMainAxis(Point mainaxis[3])
+    {
+      mainaxis[0] = mainaxis_[0];
+      mainaxis[1] = mainaxis_[1];
+      mainaxis[2] = mainaxis_[2];
+    }
+
+    /// Enquire current number of regions
+    int numRegions()
+    {
+      return (int)regions_.size();
+    }
+
+    /// Access specified region
+    shared_ptr<RevEngRegion> getRegion(int ix)
+    {
+      return regions_[ix];
+    }
+
+    /// Enquire number of identified surfaces
+    int numSurfaces()
+    {
+      return (int)surfaces_.size();
+    }
+
+    /// Access specified surface
+    shared_ptr<HedgeSurface> getSurface(int ix)
+    {
+      return surfaces_[ix];
+    }
+
+    /// Enquire minimum number of points in a region for being considered for surface
+    /// recognition
+    double getMinPointRegion()
+    {
+      return min_point_region_;
+    }
+
+    /// Store current stage for later to restart the computation from this stage (not
+    /// accessible from manageBlends2 and later)
+    void storeGrownRegions(std::ostream& os);
+
+    /// Read stored stage
+    void readGrownRegions(std::istream& is);
+
+    /// Debug functionality
+    /// Write point groups and associated surfaces to files depending on the
+    /// the number of points in the groups (uses min_point_region_ to distinguish with
+    /// respect to size)
+    void writeRegionStage(std::ostream& of, std::ostream& ofm, std::ostream& ofs) const;
+
+    /// Write point regions with an assoicated surface and the surface to file
+    void writeRegionWithSurf(std::ostream& of) const;
+
+    /// Write edges and point regions appropriated to an associated blend surface to file
+    void writeEdgeStage(std::ostream& of) const;
+    
+  private:
+    /// Characterizes the surface of the current model. Currently always set to ROUGH
+    int model_character_;
+
+    /// Triangulated surface
+    shared_ptr<ftPointSet> tri_sf_;
+
+    /// Average length of triangle edges
+    double mean_edge_len_;
+
+    /// Group points assumed to be associated one surface
+    std::vector<shared_ptr<RevEngRegion> > regions_;
+
+    /// Points with different properties than their neighbouring points.
+    /// Regions with one point can also occur
+    std::vector<RevEngPoint*> single_points_;
+
+    /// Recognized surfaces
+    std::vector<shared_ptr<HedgeSurface> > surfaces_;
+
+    /// Recognized edges. Intersection curves between surfaces with additional information
+    std::vector<shared_ptr<RevEngEdge> > edges_;
+
+    /// Final surface model including topology
+    shared_ptr<SurfaceModel> sfmodel_;
+
+    /// Bounds the point cloud (triangulated surface)
+    BoundingBox bbox_;
+    
+    /// Minimum number of neighbouring connected points for estimating point properties
+    /// (surface normal and curvature)
+    int min_next_;
+    
+    // Stop searching for neighbouring points to estimate point properties when this number
+    // is reached
+    int max_next_;
+    
+    /// Factor for radius in which to search for neighbouring points
+    double rfac_;
+
+    int edge_class_type_ = CURVATURE_EDGE;
+    int classification_type_ = CLASSIFICATION_CURVATURE;
+    
+    /// A vertex is considered an edge points if the estimated curvature radius is 
+    /// less than cfac_ times the average length of triangulation edges in the vertex
+    double cfac_;
+    
+    /// Limit for when the cone angle corresponding to triangle normals indicate an edge
+    /// Not active
+    double norm_ang_lim_;
+
+    /// Limit for when the cone angle correspondingto triangle normals indicate a plane
+    /// Not active
+    double norm_plane_lim_;  
+
+    /// When mean curvature is considered zero
+    double zero_H_;  
+
+    /// When Gauss curvature is considered zero
+    double zero_K_;
+
+    /// Minimum number of points in a region to be considered for surface recognition
+    int min_point_region_;
+
+    /// Approximation tolerance
+    double approx_tol_; 
+
+    /// Intersection tolerance
+    double int_tol_;
+
+    /// Angular tolerance used in topology build. 5.0*anglim_ is used to check whether
+    /// the estimated normal in a point corresponds to the normal of a recognized surface
+    /// in the point
+    double anglim_;
+
+    /// Used to decide if a point is an outlier
+    int max_nmb_outlier_;
+
+    /// Preferance in selecting the surface associated to a point cloud, primary surface or
+    /// best fit surface. Default is 0. 0 = always, 1 = preferred, 2 = best accuracy
+    int prefer_elementary_; 
+
+    /// Coordinated axes associated to the model
+    Point mainaxis_[3];
+
+    /// Identified model axes including position
+    std::vector<AxisInfo> model_axis_;
+
+    /// Collection of information related to smallRegionSurfaces()
+    struct SmallSurface
+    {
+      int axis_ix_, pos_ix_, lev_ix_;
+      vector<vector<RevEngPoint*> > assos_points_;
+      vector<shared_ptr<ElementarySurface> > surfs_;
+      vector<BoundingBox> bbox_;
+      int type_;   // 1 = plane1, 2=plane2, 3=rotational, 4=from remaining
+
+      SmallSurface(int ix1, int ix2, int ix3, int type,
+		   vector<shared_ptr<ElementarySurface> >& surfs)
+      {
+	axis_ix_ = ix1;
+	pos_ix_ = ix2;
+	lev_ix_ = ix3;
+	type_ = type;
+	surfs_ = surfs;
+      }
+
+      void addPoints(vector<RevEngPoint*>& points, BoundingBox bb)
+      {
+	assos_points_.push_back(points);
+	bbox_.push_back(bb);
+      }
+    };
+    
+    /// Set edge classification type. 
+    void setEdgeClassificationType(int edge_class_type)
+    {
+      edge_class_type_ = edge_class_type;
+    }
+    
+    /// Set point classification type
+    void setClassificationType(int classification_type)
+    {
+      classification_type_ = classification_type;
+    }
+
+    void setElementaryPreferLevel(int preferlevel)
+    {
+      prefer_elementary_ = preferlevel;
+    }
+    
+    void setModelCharacterization(int character)
+    {
+      //model_character_ = std::min(ROUGH, std::max(SMOOTH, character));
+      model_character_ = std::min(2, std::max(0, character));
+    }
+
+     double getInitApproxTol();
+    void edgeClassification();
+    void curvatureFilter();
+
+    void initParameters();
+    void updateParameters();
+    bool recognizeOneSurface(int& ix, int min_point_in, double angtol,
+			     int pass);
+    void recognizeSurfaces(int min_point_in, int pass);
+    void recognizeEdges(bool only_curve=false);
+    bool createBlendSurface(int ix);
+    void adjustPointRegions(int min_point_in);
+
+    void computeAxisFromCylinder(Point initaxis[3], int min_num, double max_ang,
+				 Point axis[3], int num_points[3]);
+    
+    void computeAxisFromPlane(Point initaxis[3], int min_num, double max_ang,
+			      Point axis[3], int num_points[3]);
+    
+    void surfaceExtractOutput(int idx,
+			      std::vector<std::vector<RevEngPoint*> > out_groups,
+			      std::vector<HedgeSurface*> prev_surfs);
+
+    bool segmentComposite(int& ix, int min_point_in, double angtol);
+    bool segmentByPlaneGrow(int ix, int min_point_in, double angtol);
+    bool segmentByAxis(int ix, int min_point_in);
+    bool segmentByContext(int ix, int min_point_in, double angtol, bool first);
+    void growSurface(int& ix, int pass = 1);
+    void growBlendSurface(int& ix);
+    void growMasterSurface(int& ix);
+
+    void defineSmallRegionSurfaces();
+    
+    void growSmallRegionSurface(int& ix);
+
+    bool identifySmallRotational(std::vector<RevEngPoint*>& points,
+				 Point midp, Point loc, Point axis, Point Cx,
+				 double ppar1, double ppar2,
+				 std::vector<shared_ptr<ElementarySurface> >& sfs);
+
+    bool identifySmallPlanar(std::vector<RevEngRegion*>& groups,
+			     Point loc, Point axis, Point Cx,
+			     double ppar1, double ppar2, double delta,
+			     std::vector<shared_ptr<ElementarySurface> >& sfs);
+
+    bool identifySmallPlanar(std::vector<RevEngPoint*>& groups,
+			     Point loc, Point axis, Point Cx,
+			     double ppar1, double ppar2, double delta,
+			     std::vector<shared_ptr<ElementarySurface> >& sfs);
+    
+    shared_ptr<ElementarySurface>
+    defineElemSurf(std::vector<RevEngPoint*>& points, std::vector<RevEngPoint*>& in_points,
+		   BoundingBox& bbox, std::vector<RevEngPoint*>& remain);
+
+    void planarAtPlane(shared_ptr<Plane> axis_plane,
+		       std::vector<RevEngPoint*>& points,
+		       std::vector<HedgeSurface*>& sfs,
+		       std::vector<shared_ptr<RevEngRegion> >& plane_sf_reg,
+		       std::vector<shared_ptr<HedgeSurface> >& plane_sf_hedge);
+    
+    void integrateInSmallSurfs(std::vector<shared_ptr<RevEngRegion> >& small_sf_reg,
+			       std::vector<RevEngRegion*>& nosf_reg,
+			       std::vector<RevEngRegion*>& include_reg);
+    
+    void extractSmallSurfs(SmallSurface& small_surf,
+			   std::vector<shared_ptr<RevEngRegion> >& small_sf_reg,
+			   std::vector<shared_ptr<HedgeSurface> >& small_sf_hedge,
+			   std::vector<RevEngRegion*>& nosf_reg,
+			   std::vector<RevEngPoint*>& non_assigned_pts);
+
+    void doAdaptToAxis();
+
+    bool axisUpdate(int ix, double max_ang, double angtol);
+    
+    void adjustWithMainAxis(std::vector<Point>& axes, std::vector<int>& num_pts);
+    Point planarFit(std::vector<int>& sf_ix, Point axis);
+
+    Point rotationalFit(std::vector<int>& sf_ix, Point axis, Point Cx,
+			std::vector<RevEngEdge*>& nopar_edgs);
+
+    void collectAxis(std::vector<SurfaceProperties>& sfprop);
+
+    void computeLocFunc(RevEngPoint* pt, std::vector<RevEngPoint*>& points,
+			Point& vec1, Point& vec2, double radius);
+
+    int setSmallRegionNumber();
+    
+    void storeParams(std::ostream& os) const;
+    void readParams(std::istream& is);
+    void setBoundingBox();
+    
+    void checkConsistence(std::string text) const;
+
+    std::vector<shared_ptr<RevEngEdge> >
+    defineEdgesBetween(size_t ix1,shared_ptr<ElementarySurface>& surf1,
+		       Point& dir1, size_t ix2, shared_ptr<ElementarySurface>& surf2,
+		       Point& dir2, bool only_curve=false,
+		       double lenlim=-1.0, bool check_common = true);
+    
+    shared_ptr<RevEngEdge> 
+    defineOneEdge(size_t ix1, shared_ptr<ElementarySurface>& surf1,
+		  Point& dir1, size_t ix2,
+		  shared_ptr<ElementarySurface>& surf2, Point& dir2,
+		  shared_ptr<CurveOnSurface>& int_cv1,
+		  shared_ptr<CurveOnSurface>& int_cv2,
+		  double width, std::vector<RevEngRegion*>& common_reg,
+		  bool only_curve, bool check_common);
+    
+    RevEngPoint* getDistantPoint(shared_ptr<CurveOnSurface>& cv,
+				 double tmin, double tmax, double dd,
+				 double width,
+				 std::vector<RevEngPoint*>& points);
+
+    void extendBlendAssociation(size_t ix);
+    
+    bool setBlendEdge(size_t ix);
+    
+    double
+    computeCylinderRadius(std::vector<std::vector<RevEngPoint*> > blend_pts,
+			  double width, const Point& pos, const Point& axis,
+			  const Point& dir1, const Point& dir2);
+   shared_ptr<Cylinder>
+    createCylinderBlend(std::vector<std::vector<RevEngPoint*> > blend_pts,
+			double rad1, const Point& pos, const Point& axis,
+			const Point& dir1, const Point& dir2, int sgn);
+    
+    double
+    computeTorusRadius(std::vector<std::vector<RevEngPoint*> >& blend_pts,
+		       shared_ptr<CurveOnSurface>& cv,
+		       const Point& locp, const Point& normal,
+		       shared_ptr<ElementarySurface> rotational,
+		       double width, bool plane_out, bool rot_out);
+    
+    void getTorusParameters(shared_ptr<ElementarySurface> planar,
+			    shared_ptr<ElementarySurface> rotational,
+			    double radius, int sgn1, int sgn2, double& Rrad, 
+			    Point& centre, Point& normal, Point& Cx);
+    
+    double
+    computeTorusRadius(std::vector<std::vector<RevEngPoint*> >& blend_pts,
+		       shared_ptr<CurveOnSurface>& cv,
+		       shared_ptr<ElementarySurface> elem1,
+		       shared_ptr<ElementarySurface> elem2,
+		       double width, bool out1, bool out2, int sgn,
+		       double& d2);
+    
+  bool
+    getTorusParameters(shared_ptr<ElementarySurface> elem1,
+		       shared_ptr<ElementarySurface> elem2, Point pos,
+		       double radius, double d2, bool out1, bool out2, int sgn,
+		       double& Rrad, Point& centre, Point& normal, Point& Cx,
+		       bool check_common = true);
+
+  shared_ptr<Torus>
+    torusBlend(std::vector<std::vector<RevEngPoint*> >& blend_pts,
+	       std::vector<shared_ptr<CurveOnSurface> >& cvs,
+	       const Point& locp, const Point& normal,
+	       shared_ptr<ElementarySurface> rotational,
+	       double width, bool plane_out, bool rot_out);
+    
+    shared_ptr<Torus>
+    torusBlend(std::vector<std::vector<RevEngPoint*> >& blend_pts,
+	       shared_ptr<CurveOnSurface>& cv,
+	       shared_ptr<ElementarySurface> elem1,
+	       shared_ptr<ElementarySurface> elem2,
+	       double width, bool out1, bool out2, int sgn);
+    
+    void setBlendBoundaries(RevEngRegion *reg);
+
+    void equalizeBlendRadii();
+
+    void equalizeAdjacent(size_t ix1, size_t ix2);
+    
+    void updateBlendRadius(size_t ik, double radius);
+    
+    bool defineTorusCorner(size_t ix);
+
+    void defineMissingCorner(std::vector<RevEngRegion*>& cand_adj);
+
+    bool createTorusBlend(size_t ix);
+
+    bool suitcaseCorner(std::vector<RevEngRegion*>& adj_blends,
+			RevEngEdge *rev_edge);
+
+    void extractOutPoints(std::vector<RevEngPoint*>& points, shared_ptr<ParamSurface> surf,
+			  std::vector<int>& cv_ix,
+			  double tol, double angtol,
+			  std::vector<std::vector<RevEngPoint*> >& move2adj,
+			  std::vector<RevEngPoint*>& remain);
+
+    void storeClassified(std::ostream& os) const;
+    void readClassified(std::istream& is);
+
+  };
+
+} // namespace Go
+
+#endif // _REVENG_H
diff --git a/compositemodel/include/GoTools/compositemodel/RevEngEdge.h b/compositemodel/include/GoTools/compositemodel/RevEngEdge.h
new file mode 100644
index 00000000..b995cada
--- /dev/null
+++ b/compositemodel/include/GoTools/compositemodel/RevEngEdge.h
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#ifndef _REVENGEDGE_H
+#define _REVENGEDGE_H
+
+#include "GoTools/compositemodel/RevEngRegion.h"
+#include "GoTools/geometry/CurveOnSurface.h"
+
+namespace Go
+{
+  /// Flag indicating type of associated blend surface. Only partially in use:
+  /// BLEND_NOT_SET = blend surface is expected, NOT_BLEND = sharp edge between
+  /// surfaces
+  enum
+  {
+   BLEND_NOT_SET, NOT_BLEND, STRAIGHT_BLEND, CIRCULAR_BLEND, OTHER_BLEND
+  };
+
+  /** RevEngEdge - Represents an edge between two surfaces associated RevEngRegions.
+      Information about intersection curve, adjacent regions, possible blend
+      surfaces/regions and other regions associated to this blend
+   *
+   */
+
+  class RevEngEdge
+  {
+  public:
+    /// Constructor
+    RevEngEdge();
+
+    /// Constructor giving the regions whom associated surfaces is intersected
+    /// to produce this edge
+    RevEngEdge(RevEngRegion* reg1, RevEngRegion* reg2);
+
+    /// Constructor giving information to produce an associated blend surface
+    RevEngEdge(int type, RevEngRegion* reg1, 
+	       std::vector<shared_ptr<CurveOnSurface> > cvs1,
+	       bool out1, RevEngRegion* reg2,
+	       std::vector<shared_ptr<CurveOnSurface> > cvs2,
+	       bool out2, double radius, double width);
+
+    /// Destructor
+    ~RevEngEdge();
+
+    /// Set entity identity. Used in storing of stage (see RevEng)
+    void setId(int Id)
+    {
+      Id_ = Id;
+    }
+
+    /// Enquire entity identity
+    int getId()
+    {
+      return Id_;
+    }
+
+    /// Enquire type of blend associated to edge
+    int getType()
+    {
+      return blend_type_;
+    }
+
+    /// Change first adjacent region and ensure that related information is updated
+    void setReg1(RevEngRegion *reg);
+    
+    /// Change second adjacent region and ensure that related information is updated
+    void setReg2(RevEngRegion *reg);
+
+    /// Increase the pool of regions associated to a future blend surface
+    void addBlendRegion(RevEngRegion* reg)
+    {
+      blend_regs_.push_back(reg);
+    }
+
+    /// Increase the pool of regions associated to a future blend surface
+   void addBlendRegions(std::vector<RevEngRegion*>& regs)
+    {
+      blend_regs_.insert(blend_regs_.end(), regs.begin(), regs.end());
+    }
+
+    /// Remove all regions associated to a blend surface
+    void clearBlendRegions()
+    {
+      blend_regs_.clear();
+    }
+
+    /// Remove one region associated to a blend surface
+    void removeBlendReg(RevEngRegion* reg)
+    {
+      auto it = std::find(blend_regs_.begin(), blend_regs_.end(), reg);
+      if (it != blend_regs_.end())
+	blend_regs_.erase(it);
+    }
+
+    /// Fetch geometry space curves of intersection curves between adjacent region surfaces
+    /// Expects one curve
+    std::vector<shared_ptr<ParamCurve> > getSpaceCurves();
+
+    /// Number of regions associated to a future blend surface
+    int numBlendRegs()
+    {
+      return (int)blend_regs_.size();
+    }
+
+    /// Fetch specified region associated to a future blend surface
+    RevEngRegion* getBlendReg(int ix)
+    {
+      if (ix < 0 || ix >= (int)blend_regs_.size())
+	return 0;
+      else
+	return blend_regs_[ix];
+    }
+
+    /// Fetch all regions associated to a future blend surface
+    void getAllBlendRegs(std::vector<RevEngRegion*>& blend_regs)
+    {
+      if (blend_regs_.size() > 0)
+	blend_regs.insert(blend_regs.end(), blend_regs_.begin(),
+			  blend_regs_.end());
+    }
+    
+   /// Fetch adjacent region/surfaces used to create edge
+    void getAdjacent(RevEngRegion*& reg1, RevEngRegion*& reg2)
+    {
+      reg1 = adjacent1_;
+      reg2 = adjacent2_;
+    }
+
+    /// Fetch information on how the blend surface should be placed related to the
+    /// adjacent region surfaces
+    void getOuterInfo(bool& out1, bool& out2)
+    {
+      out1 = outer1_;
+      out2 = outer2_;
+    }
+
+
+    /// Fetch intersection curves between adjacent region surfaces
+    /// \param first The curve (parameter curve) to select
+    /// Expects one curve
+    void getCurve(std::vector<shared_ptr<CurveOnSurface> >& cvs, bool first=true)
+    {
+      if (first && cvs1_.size() > 0)
+	cvs.insert(cvs.end(), cvs1_.begin(), cvs1_.end());
+      else if ((!first) && cvs2_.size() > 0)
+	cvs.insert(cvs.end(), cvs2_.begin(), cvs2_.end());
+    }
+
+    /// Get endpoints of intersection curve in geometry space
+    void getCrvEndPoints(Point& pos1, Point& pos2);
+
+    /// Fetch the distance from the intesection curve where points related to the blend
+    /// surface is to be fetched. This distance is typically larger than the final size
+    /// of the blend surface to make sure to get sufficient information to compute the blend.
+    double getDistance()
+    {
+      return distance_;
+    }
+
+    /// Fetch estimate of blend radius
+    double getRadius()
+    {
+      return radius_;
+    }
+
+    /// Set estimate of blend radius
+    void setRadius(double radius)
+    {
+      radius_ = radius;
+    }
+
+    /// The number of times an associated region surface is changed. Indicates a need for
+    /// updating the edge
+    int getSurfChangeCount()
+    {
+      return surfchangecount_;
+    }
+
+    /// An adjacent region surface has been changed
+    void increaseSurfChangeCount()
+    {
+      surfchangecount_++;
+    }
+
+    /// The edge is updated. Reset change count
+    void resetSurfChangeCount()
+    {
+      surfchangecount_ = 0;
+    }
+
+    /// The number of times an associated region is increased with more points. Indicates
+    /// a possibility for a longer edge
+    int getExtendCount()
+    {
+      return extendcount_;
+    }
+    
+    /// An adjacent region has been extended with more points
+     void increaseExtendCount()
+    {
+      extendcount_++;
+    }
+
+    /// The edge is updated. Reset edge count
+   void resetExtendCount()
+    {
+      extendcount_ = 0;
+    }
+
+    /// Set associated blend surface 
+    void setBlendRegSurf(RevEngRegion* blend)
+    {
+      defined_blend_ = blend;
+    }
+
+    /// Fetch associated blend surface 
+    RevEngRegion* getBlendRegSurf()
+    {
+      return defined_blend_;
+    }
+
+    /// Closest point between the intersection curves and the point pos
+    void closestPoint(const Point& pos, double& par, Point& close,
+		      double& dist);
+
+    /// Closest point between specified intersection curve and the point pos
+    void closestPoint(const Point& pos, double& par, Point& close,
+		      double& dist, int& ix);
+
+    /// Update intersection curve information (CurveOnSurface) when the adjacent
+    /// region reg has been associated another surface new_surf
+    void replaceSurf(RevEngRegion* reg, shared_ptr<ParamSurface>& new_surf,
+		     double tol);
+
+    /// Check consistency between geometry and parameter space curves and fix
+    /// if necessary
+    void fixMismatchCurves(double tol);
+
+    /// Remove intersection curves
+    void eraseCurves()
+    {
+      cvs1_.clear();
+      cvs2_.clear();
+    }
+
+    /// Remove information about one adjacent regions
+    void eraseAdjacent(RevEngRegion *adj)
+    {
+      if (adjacent1_ == adj)
+	adjacent1_ = 0;
+      else if (adjacent2_ == adj)
+	adjacent2_ = 0;
+    }
+
+    /// Replace intersection curves
+    void replaceCurves(std::vector<shared_ptr<CurveOnSurface> > int_cvs1,
+		       std::vector<shared_ptr<CurveOnSurface> > int_cvs2)
+    {
+      cvs1_.clear();
+      cvs2_.clear();
+      cvs1_.insert(cvs1_.end(), int_cvs1.begin(), int_cvs1.end());
+      cvs2_.insert(cvs2_.end(), int_cvs2.begin(), int_cvs2.end());
+    }
+
+    /// Check if the specified edge endpoint lies at the seam of a closed
+    /// adjacent region surface
+    int closedSfAtEnd(double tol, double& par, Point& pos, bool at_start);
+
+    /// Check if the edge is closed with respect to the tolerance tol
+    bool isClosed(double tol);
+
+    /// Check if this edge and the edge other is adjacent and in that case
+    /// return the parameter values of the joint
+    bool isAdjacent(RevEngEdge* other, double tol, double& par1, double& par2);
+
+    double startparam()
+    {
+      return cvs1_[0]->startparam();
+    }
+
+    double endparam()
+    {
+      return cvs1_[cvs1_.size()-1]->endparam();
+    }
+
+    /// Evaluate edge
+    Point point(double par);
+
+    /// Append the edge other to this one. Check if the conditions allow append, turn
+    /// curves if nesessary and update associated information
+    bool append(RevEngEdge *other, double tol);
+
+    /// Check if the parameter curve is missing for any intersection curve
+    int missingParCrv();
+
+    /// Split edge at the seam of an adjacent region surface and update associated
+    /// information accordingly.
+    /// \param added_edgs New edges due to split. As the adjacent region surfaces may have
+    /// more than one seam more than one new edge may be created
+    /// \param added_regs If a region associated to a future blend surface is divided
+    /// between two edges
+    /// \param added_sfs Not used
+    void splitAtSeam(double tol, std::vector<shared_ptr<RevEngEdge> >& added_edgs,
+		     std::vector<shared_ptr<RevEngRegion> >& added_regs,
+		     std::vector<shared_ptr<HedgeSurface> >& added_sfs);
+
+    /// Update the edge if and adjacent region is changed and extendCurve is
+    /// not applied
+    bool updateCurve(double int_tol, double tol, double len);
+
+    /// Recompute intersection curve if the adjacent regions are extended with
+    /// more points. Keep parts of the associated information
+    /// \param int_tol Tolerance used in intersection
+    /// \param tol Approximation tolerance
+    /// \param anglim Used in estimation of edge extent and to compute angular tolerance
+    /// \param len Indicates size of environment
+    /// \param lenlim Minimum length of intersection curve
+    /// \param blendlim Upper limit of with of blend surface
+    /// \param added_regions If regions are split to be associated a future blend surface
+    /// \param extract_groups If regions are split to maintain connectivity
+    /// \param out_sfs Surface to remove since the associated region has been too small
+    bool
+    extendCurve(double int_tol, double tol, double anglim, 
+		double len, double lenlim, double blendlim,
+		std::vector<shared_ptr<RevEngRegion> >& added_regions,
+		std::vector<std::vector<RevEngPoint*> >& extract_groups,
+		std::vector<HedgeSurface*>& out_sfs);
+
+    /// Define trimming curves from edges that will not lead to a blend surface.
+    /// Associate regions connected to this blend surface to the adjacent regions as
+    /// appropriate. Freed regions and eventual associated surfaces are reported in
+    /// out_regs and out_sfs
+    void setTrimCurves(double tol, double angtol,
+		       std::vector<RevEngRegion*>& out_regs,
+		       std::vector<HedgeSurface*>& out_sfs);
+
+    /// Check if the edge other is contained in this (coincidence)
+    bool contains(RevEngEdge *other, double tol);
+
+    /// Include the edge other in this (presupposes coincidence) and update parameters
+    /// accordingly
+    bool integrate(RevEngEdge *other);
+
+    /// Store edge to file
+    void store(std::ostream& os);
+
+    /// Read edge from file and collect information necessary to recreate the data structure
+    void read(std::istream& is, int& reg_id1, int& reg_id2,
+	      int& reg_id3, std::vector<int>& blend_id);
+
+  private:
+    /// Unique id for edge. Used in storing and reading data structure to and from file
+    int Id_;
+
+    /// First regions where the region surface is intersected to produce this edge
+    RevEngRegion* adjacent1_;
+
+    /// Second regions where the region surface is intersected to produce this edge
+    RevEngRegion* adjacent2_;
+
+    /// Blend surface associated to the intersection curve between region surfaces
+    /// represented in this edge
+    RevEngRegion* defined_blend_;
+
+    /// Intersection curve with parameter curve in region surface one
+    std::vector<shared_ptr<CurveOnSurface> > cvs1_;
+    
+    /// Intersection curve with parameter curve in region surface two
+    std::vector<shared_ptr<CurveOnSurface> > cvs2_;
+
+    /// Regions associated to a future blend surface corresponding to this edge
+    std::vector<RevEngRegion*> blend_regs_;
+
+    /// Type of blend: currently BLEND_NOT_SET or NOT_BLEND
+    int blend_type_;
+    
+    /// The distance from the interection curve in which to search for points associated
+    /// to the blend surface
+    double distance_;
+
+    /// Estimate of blend radius
+    double radius_;
+
+    /// How a blend surface will be placed relative to the outside of region surface one
+    bool outer1_;
+
+    /// How a blend surface will be placed relative to the outside of region surface two
+    bool outer2_;
+
+    /// Whether any of the adjacent region surfaces is changed
+    int surfchangecount_;
+
+    /// Whether any of the adjacent regions is extended with more points
+    int extendcount_;
+
+    shared_ptr<RevEngEdge> doSplit(size_t ix, int side, double par, double tol,
+				   std::vector<shared_ptr<RevEngRegion> >& added_regs,
+				   std::vector<shared_ptr<HedgeSurface> >& added_sfs);
+    
+    void updateParCurve(RevEngRegion* adj, double int_tol);
+
+  };
+}
+
+#endif
diff --git a/compositemodel/include/GoTools/compositemodel/RevEngPoint.h b/compositemodel/include/GoTools/compositemodel/RevEngPoint.h
new file mode 100644
index 00000000..744fec54
--- /dev/null
+++ b/compositemodel/include/GoTools/compositemodel/RevEngPoint.h
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#ifndef _REVENGPOINT_H
+#define _REVENGPOINT_H
+
+#include "GoTools/compositemodel/ftPointSet.h"
+//#include "GoTools/compositemodel/RevEngRegion.h"
+#include "GoTools/utils/Array.h"
+#include "GoTools/utils/Point.h"
+#include "GoTools/utils/DirectionCone.h"
+
+namespace Go
+{
+  class RevEngRegion;
+  
+  /** RevEngPoint - A triangulation vertex enhanced with with information such as estimated 
+      surface normal and curvature, classification flags and associated functionality. 
+      A RevEngPoint knows which RevEngRegion it belongs to, if any. The reverse engineering
+      functionality where RevEngPoint participates is run from RevEng.
+   *
+   */
+  // Enumerations representing classification results
+
+  /// Edge classification based on covariance. Not used
+  enum
+  {
+   PCA_EDGE_UNDEF, PCA_NOT_EDGE, PCA_CLOSE_EDGE, PCA_EDGE
+  };
+
+  /// Edge classification based on curvature radius. Used
+  enum
+  {
+   C1_EDGE_UNDEF, C1_NOT_EDGE, C1_CLOSE_EDGE, C1_EDGE
+  };
+
+  enum
+  {
+   C2_EDGE_UNDEF, C2_NOT_EDGE, C2_CLOSE_EDGE, C2_EDGE
+  };
+
+  /// Point classification based on covariance. Not used
+  enum
+  {
+   PCA_UNDEF, PCA_PLANAR, PCA_LINEAR, PCA_OTHER
+  };
+
+  /// Point classification based on curvature. Used
+  enum
+  {
+   C1_UNDEF, C1_PEAK, C1_RIDGE, C1_SRIDGE, C1_NONE, C1_FLAT, C1_MINSURF, C1_PIT, C1_VALLEY, C1_SVALLEY 
+  };
+    
+  /** Subclass of ftSamplePoint; Enhanced point for use in reverse engineering
+   */
+  class RevEngPoint : public ftSamplePoint
+  {
+
+  public:
+    /// Constructor
+    RevEngPoint();
+
+    /// Constuctor given point position
+    /// \param bnd Boundary information. Inherited from ftSamplePoint. Not used
+   RevEngPoint(Vector3D xyz, int bnd);
+
+    /// Destructor
+    virtual ~RevEngPoint();
+
+    /// Compute normal based on the triangles meeting in the vertex
+    /// \param lim Avoid very long triangle edges
+    void computeTriangNormal(double lim);
+
+    /// Set eigen vector and values in point
+    void addCovarianceEigen(Point& eigen1, double lambda1, Point& eigen2,
+			    double lambda2, Point& eigen3, double lambda3);
+
+    /// Set normal and curvature information computed from expressing a group of points
+    /// in the vicinity of the current in the coordinate system given by the eigen vector
+    /// and approximate the points with a Bezier function. Surface normal minimum and
+    /// maximum principal curvature vectors and values are computed as well as
+    /// the distance from this point to the function and the average distance for all points
+    /// in the group
+    void addLocFuncInfo(Point& norm, Point& mincvec, double minc, Point& maxcvec,
+		      double maxc, double currdist, double avdist);
+
+    /// Enquire triangulation normal
+    Point getTriangNormal()
+    {
+      Point zero(0.0,0.0,0.0);
+      return normalcone_.greaterThanPi() ? zero :
+	      normalcone_.centre();
+    }
+
+    /// Enquire the cone angle of a cone surrounding the surface normal of all
+    /// triangles meeting in this vertex
+    double getTriangAngle()
+    {
+      return normalcone_.greaterThanPi() ? 2.0*M_PI : normalcone_.angle();
+    }
+
+    /// Enquire the average length of triangle edges meeting in this vertex
+    double getMeanEdgLen();
+
+    /// Enquire the average length of triangle edges meeting in this vertex excluding
+    /// edges with a length larger than maxlen
+    double getMeanEdgLen(double maxlen);
+
+    /// Enquire the normal computed from a local function approximation
+    const Point& getLocFuncNormal()
+    {
+      return LocFuncnormal_;
+    }
+
+    /// Turn the normal computed from a local function approximation
+    void turnLocFuncNorm()
+      {
+	LocFuncnormal_ *= -1;
+      }
+
+
+    /// Enquire first eigen vector
+    const Point& getPCAEigen1()
+    {
+      return eigen1_;
+    }
+
+    /// Enquire normal given by covariance, e.g. the third eigen vector
+     const Point& getPCANormal()
+    {
+      return eigen3_;
+    }
+
+    /// Turn direction of the coordinate system given by eigen vector
+    void turnPCA()
+    {
+      eigen2_ *= -1;
+      eigen3_ *= -1;
+    }
+
+
+
+    /// Traverse the triangulation and fetch points belonging to a given region, if given,
+    /// within the sphere with radius radius from this vertex 
+    /// \param min_nmb Minimum number of points required. The radius is
+    /// increased if necessary to reach this number of points
+    /// \param max_nmb When this number of points is reached, the search is stopped
+   void fetchClosePoints2(double radius, int min_nmb, int max_nmb,
+			   std::vector<RevEngPoint*>& nearpts,
+			   RevEngRegion *region = 0);
+
+    /// Fetch points belonging to the specified region connected through triangle edges,
+    /// starting from current. Stop when max_nmb points are reached
+    void fetchConnected(RevEngRegion *region, int max_nmb,
+			std::vector<RevEngPoint*>& group);
+
+     /// Fetch points marked with mark connected through triangle edges,
+    /// starting from current. 
+   void fetchConnectedMarked(int mark,
+			      std::vector<RevEngPoint*>& group);
+
+    /// Indicate that the points has been visited in a traversal
+    void setVisited()
+    {
+      visited_ = 1;
+    }
+
+    /// A traversal is finished. Reset flag
+    void unsetVisited()
+    {
+      visited_ = 0;
+    }
+
+    /// Check if a point has been visited in a traversal
+    bool visited()
+    {
+      return (visited_ > 0);
+    }
+
+    /// Indicate that the point has been moved from one region to another. 
+    void setMoved()
+    {
+      moved_ = 1;
+    }
+
+    /// Reset move flag
+    void unsetMoved()
+    {
+      moved_ = 0;
+    }
+
+    /// Check if a ponit has been moved between regions
+    bool moved()
+    {
+      return (moved_ > 0);
+    }
+
+    /// The eigen vectors and values are averaged between several computations.
+    /// The number of computations are returned
+    int nmbEigen()
+    {
+      return nmb_eigen_;
+    }
+    
+    /// The results of the computation of normal and curvature by local funcations are
+    /// averaged between several computations. The number of computations are returned
+    int nmbLocFunc()
+    {
+      return nmb_locfunc_;
+    }
+
+    /// Enquire maximum principal curvature value
+    double maxPrincipalCurvature()
+    {
+      return kmax_;
+    }
+
+    /// Enquire maximum principal curvature vector
+    Point maxCurvatureVec()
+    {
+      return kvecmax_;
+    }
+
+    /// Enquire miniumum principal curvature value
+    double minPrincipalCurvature()
+    {
+      return kmin_;
+    }
+
+    /// Enquire miniumum principal curvature vector
+    Point minCurvatureVec()
+    {
+      return kvecmin_;
+    }
+
+    /// Enquire Gauss curvature
+    double GaussCurvature()
+    {
+      return gausscurv_;
+    }
+
+    /// Enquire mean curvature
+    double meanCurvature()
+      {
+	return meancurv_;
+      }
+
+    /// Gauss and mean curvature are smoothed to get less fragmented segmentation.
+    /// Enquire previous value of Gauss curvature
+    double GaussCurvature0()
+    {
+      return gausscurv0_;
+    }
+
+    /// Gauss and mean curvature are smoothed to get less fragmented segmentation.
+    /// Enquire previous value of mean curvature
+    double meanCurvature0()
+      {
+	return meancurv0_;
+      }
+
+    /// Set mean curvature. Used in smoothing of mean curvature
+    void setMeanCurvature(double mean)
+    {
+      meancurv_ = mean;
+    }
+
+    /// Set Gauss curvature. Used in smoothing of Gauss curvature
+    void setGaussCurvature(double Gauss)
+    {
+      gausscurv_ = Gauss;
+    }
+
+    /// Update curvature values. Used in iteration for smoothing Gauss and mean curvature
+    void updateCurvature()
+    {
+      meancurv0_ = meancurv_;
+      gausscurv0_ = gausscurv_;
+    }
+
+    double getCurvedness()
+    {
+      return curvedness_;
+    }
+
+
+    /// Set edge status in point using curvature radius
+    void setEdgeClassification(int c1_edge)
+    {
+      edge_[0] = PCA_EDGE_UNDEF;
+      edge_[1] = c1_edge;
+      edge_[2] = C2_EDGE_UNDEF;
+    }
+
+    /// Set point classification using mean and Guass curvature
+    void setClassification(int ctype)
+    {
+      surf_[0] = PCA_UNDEF;
+      surf_[1] = ctype;
+    }
+
+    /// Check if a point is classified as edge. edge_class_type is expected to be C1_EDGE
+    bool isEdge(int edge_class_type)
+    {
+      if (edge_class_type == 1)
+	return (edge_[0] == PCA_EDGE);
+      else if  (edge_class_type == 2)
+	return (edge_[1] == C1_EDGE);
+      else if  (edge_class_type == 3)
+	return (edge_[2] == C2_EDGE);
+      else
+	return false;
+    }
+
+    /// Check if a point near being classified as edge. edge_class_type is expected to be C1_EDGE
+    bool closeEdge(int edge_class_type)
+    {
+      if (edge_class_type == 1)
+	return (edge_[0] >= PCA_CLOSE_EDGE);
+      else if  (edge_class_type == 2)
+	return (edge_[1] >= C1_CLOSE_EDGE);
+      else if  (edge_class_type == 3)
+	return (edge_[2] >= C2_CLOSE_EDGE);
+      else
+	return false;
+    }
+
+     /// Check if a point is different from an edge. edge_class_type is expected to be C1_EDGE
+    bool notEdge(int edge_class_type)
+    {
+      if (edge_class_type == 1)
+	return (edge_[0] <= PCA_NOT_EDGE);
+      else if  (edge_class_type == 2)
+	return (edge_[1] <= C1_NOT_EDGE);
+      else if  (edge_class_type == 3)
+	return (edge_[2] <= C2_NOT_EDGE);
+      else
+	return true;
+    }
+
+    /// Check if a point is classified as edge, but lacks other edge points
+    /// in its vicinity
+    bool isolatedEdge(int edge_class_type, int nmb, bool close);
+
+    /// Unset edge information in point
+    void setEdgeUndef()
+    {
+      edge_[0] = PCA_EDGE_UNDEF;
+      edge_[1] = C1_EDGE_UNDEF;
+      edge_[2] = C2_EDGE_UNDEF;
+    }
+
+    /// Get distance between point and the local function used to compute normal and
+    /// curvature information
+    double getPointDistance()
+    {
+      return ptdist_;
+    }
+
+    /// Get average distance between local point sets and the function used to compute normal 
+    /// and curvature information
+    double getAveragePointDistance()
+    {
+      return avdist_;
+    }
+
+    /// Enquire the point classification with respect to expected surface type
+    int surfaceClassification(int classification_type) const;
+
+    /// A point classified as edge is reclassified as near edge if the triangles meeting
+    /// in the point don't indicate an edge
+    void adjustWithTriangNorm(double anglim);
+
+    /// Check if the point is assigned a region
+    bool hasRegion()
+    {
+      return (region_ != 0);
+    }
+
+    /// Enquire the region assigned to the point
+    RevEngRegion* region()
+    {
+      return region_;
+    }
+
+    /// Set region in point
+    void setRegion(RevEngRegion* region)
+    {
+      region_ = region;
+    }
+
+    /// Unset region information
+    void unsetRegion()
+    {
+      region_ = 0;
+    }
+
+    /// Unset accuracy and parameter information with respect to the surface
+    /// approximating the points in the associated region
+    void unsetSurfInfo()
+    {
+      sfdist_ = -1.0;
+      sfang_ = -1.0;
+      uv_ = Vector2D(0.0, 0.0);
+    }
+
+    /// Fetch regions different from the region of this point associated to the neighbouring
+    /// points of this point
+    void adjacentRegions(std::vector<RevEngRegion*>& adj) const;
+
+    /// Check if any neighbour is associated the region reg
+    bool nextToRegion(RevEngRegion *reg);
+
+    /// Return classification of of point using Gauss and mean curvature 
+    int C1_surf()
+    {
+      return surf_[1];
+    }
+
+    /// Mark point as outlier
+    void setOutlier()
+    {
+      outlier_ = true;
+    }
+
+    /// Reset outlier information
+    void unsetOutlier()
+    {
+      outlier_ = false;
+    }
+
+    /// Check if a point is classified as outlier
+    bool isOutlier()
+    {
+      return outlier_;
+    }
+
+    /// The number of neighbours having the same classification as this point
+    int nmbSameClassification(int classification_type) const;
+
+    /// Record the distance between this point and the surface approximating the points
+    /// of the associated region as well as the angular difference between surface normal
+    /// and normal in point (least angle with respect to triangulation normal and local
+    /// function normal)
+    void setSurfaceDist(double dist, double ang)
+    {
+      sfdist_ = dist;
+      sfang_ = ang;
+    }
+
+    /// Enquire distance to approximating surface
+    double  getSurfaceDist()
+    {
+      return sfdist_;
+    }
+    
+    /// Enquire distance and normal difference with respect to approximating surface
+    void getSurfaceDist(double& dist, double& ang)
+    {
+      dist = sfdist_;
+      ang = sfang_;
+    }
+
+    /// Check if any neighbouring vertex is associated the region reg
+    bool isNeighbour(RevEngRegion* reg) const;
+
+    /// Check if this vertex and pt are neighbours
+    bool isNeighbour(RevEngPoint* pt) const;
+
+    /// Fetch regions associated to neighbouring points that are associated a surface
+    std::vector<RevEngRegion*> adjacentRegsWithSurf() const;
+
+    /// Increase the number of moves between regions performed by this point
+    void addMove()
+    {
+      nmb_move_++;
+    }
+
+    /// Enquire the number of moves between regions performed by this point
+    /// (not complete information)
+    int numMove()
+    {
+      return nmb_move_;
+    }
+
+    /// Fetch adjacent regions to this point and the closest point in those
+    /// regions. Distant regions/points are excluded (distance depends on mean_edge_len)
+    void getAdjInfo(double mean_edge_len, std::vector<RevEngRegion*>& adj_reg,
+		    std::vector<RevEngPoint*>& adj_pt);
+
+    /// Fetch regions associated to neighbouring points 
+    std::vector<RevEngRegion*> getAdjacentRegions() const;
+
+    
+    /// Number of different regions associated to neighbouring points 
+    int numAdjacentRegions() const;
+
+    /// Include point in adjacent regions if the distance to this region is suffiently
+    // small and the point normal information is consistent
+    bool mergeWithAdjacent(double mean_edge_len);
+
+    /// Set flag used in region growing and identification of connected groups
+    void setMarkIx(int ix)
+    {
+      mark_ix_ = ix;
+    }
+
+    /// Unset flag used in region growing and identification of connected groups
+    void unsetMarkIx()
+    {
+      mark_ix_ = -1;
+    }
+
+    /// Enquire flag used in region growing and identification of connected groups
+    int getMarkIx()
+    {
+      return mark_ix_;
+    }
+
+    /// Store enhanced vertex information to file
+    void store(std::ostream& os) const;
+
+    /// Read enhanced vertex information from file. Return information about neighbouring vertices
+    void read(std::istream& is, vector<int>& next_ix);
+    
+  private:
+    /// Average length of edges meeting in this vertex
+    double avedglen_;
+    
+    /// Eigenvectors of covariance matrix
+    Point eigen1_, eigen2_, eigen3_;
+
+    /// Eigenvalues of covariance matrix. lambda1_>= lambda2_ >= lambda3_
+    double lambda1_, lambda2_, lambda3_;
+    
+    /// Normal and principal curvature vectors computed from local function approximation
+    Point LocFuncnormal_, kvecmin_, kvecmax_;
+
+    /// Principal curvatures computed from local function approximation
+    double kmin_, kmax_;
+
+    /// Distance to local function
+    double ptdist_, avdist_;
+
+    /// Number of point groups averaged to compute eigen vector/values, normal and
+    /// curvature information
+    int nmb_eigen_, nmb_locfunc_;
+
+    /// Span of surface normals computed from triangulation
+    DirectionCone normalcone_;  
+
+    /// Previous and current version of mean curvature
+    double meancurv0_, meancurv_;
+
+    /// Previous and current version of Gauss curvature
+    double gausscurv0_, gausscurv_;
+
+    /// Curvedness computed from principal curvatures. Not used
+    double curvedness_;   
+
+    /// Parameters corresponding to classification results
+    /// Results of edge classification. Sequence: Surface variation (PCA), curvature (C1),
+    /// curveness (C2)
+    int edge_[3];
+    
+    /// Results of surface classification. Sequence: PCA, curvature (C1), 
+    int surf_[2];   // 
+    
+    /// Group (segment) of classified points
+    RevEngRegion* region_;
+
+    /// Outlier flag
+    bool outlier_;
+
+    /// Distance beteen point and surface approximating associated region
+    double sfdist_;
+
+    /// Angle between estimated normal in point and surface normal
+    double sfang_;
+
+    /// Number of times this point has been moved between region. Uncomplete information
+    int nmb_move_;
+
+    /// Flags mainly used in traversal
+    mutable int visited_;
+    mutable int moved_;
+    mutable int mark_ix_;
+
+    // Traverse the triangulation and fetch points within the sphere with
+    // radius radius from this vertex 
+    Point fetchClosePoints(double radius, int min_nmb, int max_nmb,
+			   std::vector<Point>& nearpts);
+    void getNearby(Vector3D xyz, double radius, int max_nmb,
+		   std::vector<RevEngPoint*>& near,
+		   RevEngRegion *region = 0);
+  };
+}
+
+#endif
diff --git a/compositemodel/include/GoTools/compositemodel/RevEngRegion.h b/compositemodel/include/GoTools/compositemodel/RevEngRegion.h
new file mode 100644
index 00000000..b2831f97
--- /dev/null
+++ b/compositemodel/include/GoTools/compositemodel/RevEngRegion.h
@@ -0,0 +1,1856 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#ifndef _REVENGREGION_H
+#define _REVENGREGION_H
+
+#include "GoTools/compositemodel/RevEngPoint.h"
+#include "GoTools/compositemodel/ImplicitApprox.h"
+#include "GoTools/utils/BoundingBox.h"
+#include "GoTools/geometry/ClassType.h"
+//#include "GoTools/compositemodel/HedgeSurface.h"
+#include <set>
+
+
+namespace Go
+{
+  class HedgeSurface;
+  class ftEdge;
+  //class RevEngPoint;
+  class Circle;
+  class SplineCurve;
+  class CurveOnSurface;
+  class Plane;
+  class Cylinder;
+  class Sphere;
+  class Cone;
+  class Torus;
+  class SplneSurface;
+  class RevEngEdge;
+  
+  /// Method to classify points (vertices). Default and currently the only choice: CLASSIFICATION_CURVATURE
+  enum
+    {
+     CLASSIFICATION_UNDEF, CLASSIFICATION_CURVATURE, CLASSIFICATION_SHAPEINDEX, CLASSIFICATION_POINTASSOCIATION
+    };
+
+  /// Method used in edge classification. Default and currently the only choice: CURVATURE_EDGE
+  enum
+    {
+     TRIANGULATION_EDGE, PCATYPE_EDGE, CURVATURE_EDGE, CNESS_EDGE, RPFAC_EDGE
+    };
+
+  /// Preference for elementary. Default and recommended choice: ALWAYS_ELEM
+  enum
+    {
+     ALWAYS_ELEM, PREFER_ELEM, BEST_ACCURACY
+    };
+
+  /// Surface flag. Quality of approximation.
+  /// ACCURACY_OK: Requirements met for distance between points and surface and angle between normals
+  /// ANGULAR_DEVIATION: Requirements met for distance between points and surface 
+  /// PROBABLE_HELIX: As for ANGULAR_DEVIATION in the cylinder and cone case
+  /// ACCURACY_POOR: Approximation accuracy not met, but the surface is relatively close to the points
+  /// NOT_SET: No surface or large distance
+  enum
+    {
+     ACCURACY_OK, ANGULAR_DEVIATION, PROBABLE_HELIX, ACCURACY_POOR, NOT_SET
+    };
+
+  /// Surface history. Whether or not the surface is modified after first computation
+  enum
+    {
+     INITIAL, AXIS_ADAPTED, ADJACENT_ADAPTED
+    };
+
+  /// Nessecary information to create a swept spline surface. Currently disabled
+  struct SweepData
+  {
+    int type_;  // Linear = 1, rotational = 2, cylinderlike = 3, conelike = 4
+    shared_ptr<SplineCurve> profile_;
+    Point location_;
+    Point added_info_;
+    double radius_;
+    double angle_;
+    double maxdist_;
+    double avdist_;
+    int num_in_;
+
+    SweepData(int type, shared_ptr<SplineCurve> profile, Point location, 
+	      Point info2, double maxdist, double avdist, int num_in,
+	      double radius = -1.0, double angle = 0.0)
+    {
+      type_ = type;
+      profile_ = profile;
+      location_ = location;
+      added_info_ = info2;
+      maxdist_ = maxdist;
+      avdist_ = avdist;
+      num_in_ = num_in;
+      radius_ = radius;
+      angle_ = angle;
+    }
+  };
+
+  struct SegmentData
+  {
+    int type_; // Around axis = 1
+    Point loc_;
+    Point axis_;
+    double min_dist_;
+    double max_dist_;
+
+    SegmentData(int type, Point& loc, Point& axis, double mind, double maxd)
+    {
+      type_ = type;
+      loc_ = loc;
+      axis_ = axis;
+      min_dist_ = mind;
+      max_dist_ = maxd;
+    }
+  };
+  
+  /** RevEngRegion - Point groups after segmenting a triangulated surface in regions believed
+      to be appropriate for representaton by one CAD type surface. The regions are dynamic, points
+      are removed and added as appropriate. Class entities are primarily accessed by RevEng, but
+      also from RevEngEdge, RevEngPoint and HedgeSurface
+   * 
+   */
+
+  class RevEngRegion
+  {
+  public:
+    /// Constructor
+    RevEngRegion(int edge_class_type);
+
+    RevEngRegion(int classification_type, int edge_class_type);
+
+    /// Constructor given a group of RevEngPoints
+    RevEngRegion(int classification_type, int edge_class_type,
+		 std::vector<RevEngPoint*>& points);
+
+    /// Destructor
+    ~RevEngRegion();
+
+    /// Set entity identity. Used in storing of stage (see RevEng)
+    void setId(int Id)
+    {
+      Id_ = Id;
+    }
+
+    /// Enquire entity identity
+    int getId()
+    {
+      return Id_;
+    }
+
+    /// Enquire type
+     int getClassificationType()
+    {
+      return classification_type_;
+    }
+
+     int getEdgeClassificationType()
+    {
+      return edge_class_type_;
+    }
+
+    /// Check if the group is compatible with a surface type. NB! Not stable information
+    bool isCompatible(ClassType classtype, int sfcode);
+    
+    /// Extend group with one point
+    void addPoint(RevEngPoint* point);
+
+    /// Extend group with points.
+    /// NB! The group must be associated a surface
+    /// NB! No testing on whether the points actually belongs to this group.
+    /// Accuracy statistics is computed
+    bool addPointsToGroup(std::vector<RevEngPoint*>& points,
+			  double tol, double angtol, bool compute_accuracy = true);
+    
+    /// Remove one point. NB! Leaves overview information invalid.
+    void removePoint(RevEngPoint* point);
+
+    /// Extend current region with the points of another region. Update class
+    /// parameters
+    /// \param dist_ang If given: update new points with distance to surface and angular
+    /// deviation between normals
+    void addRegion(RevEngRegion* reg,
+		   std::vector<std::pair<double, double> >& dist_ang,
+		   double maxd=0.0, double avd=0.0, int num_inside=-1,
+		   int num_inside2=-1);
+    
+    /// Update class parameters dependent on point properties
+    void updateInfo(double tol=-1.0, double angtol=-1.0);
+
+    /// Extend region with adjacent points having the same classification
+    void collect(RevEngPoint *pt, RevEngRegion* prev=0);
+
+    /// Enquire number of points in region
+    int numPoints()
+    {
+      return (int)group_points_.size();
+    }
+
+    /// Get specified point
+    RevEngPoint* getPoint(int ix)
+    {
+      if (ix < 0 || ix >= (int)group_points_.size())
+	return 0;
+      else
+	return group_points_[ix];
+    }
+
+    /// Iterator to start of points in region
+    std::vector<RevEngPoint*>::iterator pointsBegin()
+    {
+      return group_points_.begin();
+    }
+    
+   /// Iterator past points in region
+    std::vector<RevEngPoint*>::iterator pointsEnd()
+    {
+      return group_points_.end();
+    }
+
+    /// Return all points
+    std::vector<RevEngPoint*> getPoints()
+    {
+      return group_points_;
+    }
+
+    /// Return bounding box surrounding all points
+    const BoundingBox& getBbox()
+    {
+      return bbox_;
+    }
+
+    /// Return bounding box surrounding the parameter points corresponding to the
+    /// points. Only if the region is associated a surface
+    BoundingBox getParameterBox();
+
+    /// Enquire the fraction of LocFunc normals that is closer to the average LocFunc normal
+    /// in the region than the angular tolerance (used to identify possible planar regions)
+    double getFracNorm()
+    {
+      return frac_norm_in_;
+    }
+    
+   /// Extract a sub group of points that can be approximated by a plane and compute the plane
+    /// \param mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param plane_pts Extracted points
+    /// \param plane_out Computed plane (if a sufficient number of points are found)
+    void growLocalPlane(Point mainaxis[3], double tol,
+			std::vector<RevEngPoint*>& plane_pts,
+			shared_ptr<Plane>& plane_out);
+
+    /// If the region is associated a plane, a cylinder or a cone, extend the point group
+    /// with points from adjacent regions if feasible. It is ensured that all affected
+    /// regions are connected
+    /// \param mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param grown_regions Regions completely absorbed in the current
+    /// \param adj_surfs Surfaces associated with the grown regions
+    /// \param added_groups Connected groups of points extracted from adjacent regions
+    /// in order to maintain connectivity for these regions
+    void growPlaneOrCyl(Point mainaxis[3], int min_pt_reg,
+			double tol, double angtol,
+			std::vector<RevEngRegion*>& grown_regions,
+			std::vector<HedgeSurface*>& adj_surfs,
+			std::vector<std::vector<RevEngPoint*> >& added_groups);
+
+    /// Extract points with a significant distance to the surface and a large angular deviation
+    /// \param added_groups Connected sub groups of removed points
+    void removeLowAccuracyPoints(int min_pt_reg, double tol, double angtol,
+				 std::vector<std::vector<RevEngPoint*> >& added_groups);
+
+    /// Include adjacent region is current provided that current is associated with
+    /// a surface and that the accuracy is sufficient. Accuracy towards surface is computed
+    /// \param adj Region to include
+    /// \param mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param angtol Angular tolerance
+    /// \param grown_regions Region absorbed in the current
+    /// \param adj_surfs Surface associated with the grown region
+    /// \param return Whether or not the region is included
+    bool includeAdjacent(RevEngRegion* adj, Point mainaxis[3], 
+			 double tol, double angtol,
+			 std::vector<RevEngRegion*>& grown_regions,
+			 std::vector<HedgeSurface*>& adj_surfs);
+
+    /// Grow current region with adjacent regions. Current region must be associated
+    /// with a surface and the accuracy must be sufficient
+    /// \param mainaxis Local coordinate system for model
+    /// \param min_pt_reg Associated with surface flag. Not used
+    /// \param tol Approximation tolerance
+    /// \param angtol Angular tolerance
+    /// \param grown_regions Regions absorbed in the current. To be removed in RevEng
+    /// \param adj_surfs Surfaces associated with the grown regions
+    /// \param adj_edgs Surfaces associated with the grown regions
+    void growWithSurf(Point mainaxis[3], int min_pt_reg,
+		      double tol, double angtol,
+		      std::vector<RevEngRegion*>& grown_regions,
+		      std::vector<HedgeSurface*>& adj_surfs,
+		      std::vector<RevEngEdge*>& adj_edgs,
+		      bool use_base=false);
+
+    /// Grow current region associated with a blend surface with adjacent regions not associated
+    /// any surface. The accuracy must be sufficient
+    void growBlendSurf(std::vector<RevEngRegion*>& next_blend, double tol,
+		       double angtol, std::vector<RevEngRegion*>& grown_regions,
+		       std::vector<std::vector<RevEngPoint*> >& added_regions);
+
+    /// Check potential accuracy of integrating the region other into current.
+    /// Current must be associated a surface
+    int getGrowAccuracy(RevEngRegion *other, double tol,
+			double angtol, double& maxdist,
+			double& avdist, int& num_in, int& num2_in,
+			double& maxdist2, double& avdist2, int& num_in2,
+			int& num2_in2, std::vector<double>& parvals,
+			std::vector<std::pair<double,double> >& distang);
+
+    /// If feasible, merge adjacent regions without an associated surface into current.
+    /// Current is not expected to have an associated surface. The combined regions must
+    /// be possible to approximate with a plane
+    bool mergePlanarReg(double zero_H, double zero_K, double tol,
+			Point mainaxis[3],
+			std::vector<RevEngRegion*>& grown_regions);
+
+    /// Include adjacent regions with the same type of associated surface into current
+    /// if the accuracy is sufficient. The current surface may be updated
+    void mergeAdjacentSimilar(double tol, double angtol,
+			      std::vector<RevEngRegion*>& grown_regions,
+			      std::vector<HedgeSurface*>& adj_surfs,
+			      std::vector<RevEngEdge*>& adj_edgs);
+
+    /// Try to approximate the region points with a plane, a cylinder or a cone.
+    /// If a surface is recognized will deviant points be extracted
+    void
+    initPlaneCyl(int min_point, int min_pt_reg,
+		 double tol, double angtol, Point mainaxis[3],
+		 double zero_H, double zero_K,
+		 std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+		 std::vector<vector<RevEngPoint*> >& out_groups,
+		 std::vector<RevEngPoint*>& single_pts, bool& repeat);
+    
+    /// Not connected parts of the region is extracted and split into
+    /// connected sub groups
+    void splitRegion(std::vector<std::vector<RevEngPoint*> >& separate_groups);
+
+    /// Split region with planar points according to the direction of the point normals.
+    /// To divide the region into groups feasible for being represented by planes with
+    /// varying normal
+    void splitPlanar(double lim_cone, int min_point_reg, 
+		     std::vector<std::vector<RevEngPoint*> >& other_groups,
+		     std::vector<RevEngPoint*>& single);
+    
+    /// Include adjacent regions into current if the accuracy is sufficient. The current surface
+    /// is not updated
+    void joinToCurrent(double tol, double angtol, int small_lim,
+		       std::vector<RevEngRegion*>& adapted_regions);
+
+    /// Merge adjacent regions if consistent. The regions are not associated a surface.
+    /// Time consuming and currently not in use
+    void joinRegions(Point mainaxis[3], double approx_tol, double anglim,
+		     std::vector<RevEngRegion*>& adapted_regions);
+
+    /// Extract points with deviant accuracy. The removed points are divided into
+    /// connected groups. The current region must be associated a surface
+    void extractOutPoints(std::vector<std::pair<double, double> >& dist_ang,
+			  double tol, double angtol,
+			  std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+    /// Remove specified points from the current region. Information related to the
+    /// region in the points is unset
+    void removeAndUpdatePoints(vector<RevEngPoint*>& points);
+
+    /// Identify points associated to a blend surface corresponding to an edge
+    /// between this region and another region. 
+    void extractOutOfEdge(shared_ptr<CurveOnSurface>& cv,
+			  std::vector<shared_ptr<CurveOnSurface> >& intcv,
+			  double radius, double tol, double angtol,
+			  std::vector<RevEngPoint*>& out_points);
+
+    /// Identify points on the "other side" of an edge between this region and another
+    /// region. 
+    void extractOutOfEdge2(std::vector<shared_ptr<CurveOnSurface> >& intcv,
+			   double tol, double angtol,
+			  std::vector<RevEngPoint*>& out_points);
+
+    /// Identify points with deviant accuracy. The limit for being deviant is
+    /// set dependent on tol and avd
+    void identifyDistPoints(std::vector<std::pair<double, double> >& dist_ang,
+			    double tol, double maxd, double avd,
+			    std::vector<RevEngPoint*>& dist_points);
+
+    /// Check if the region points are connected trough edges
+    bool isConnected();
+
+    /// Split into connected groups
+    /// \param move A sub set of the region points to check for connectivity.
+    /// \param out_groups Connected sub groups of move
+    /// \param outer Whether or not only sub groups of move at the boundary of this
+    /// regions is to be added to out_groups
+    /// \param inner The remaining points in move if outer = true
+   void connectedGroups(std::vector<RevEngPoint*>& move,
+			std::vector<std::vector<RevEngPoint*> >& out_groups,
+			bool outer, std::vector<RevEngPoint*>& inner);
+
+    /// Extract specified points from the region. The specified points are
+    /// divided into connected groups. If outer=true, points from move internal to
+    /// the regions are kept
+   void extractSpesPoints(std::vector<RevEngPoint*>& move,
+			   std::vector<std::vector<RevEngPoint*> >& out_groups,
+			   bool outer=false);
+
+    /// Extract points with deviant accuracy. The removed points are divided into
+    /// connected groups. The current region must be associated a surface
+    void extractOutPoints(int dir, double tol, double angtol,
+			  double angtol2,
+			  std::vector<std::vector<RevEngPoint*> >& out_groups);
+    
+    /// Remove specified points from the current region. Points are not updated
+    void removePoints(std::vector<RevEngPoint*>& remove);
+
+    /// Enquire region classification. Unstable information
+   int getClassification()
+    {
+      if (classification_type_ == CLASSIFICATION_CURVATURE)
+	return group_points_[0]->C1_surf();
+      else
+	return -1;
+    }
+
+    /// Check if the regions is classified for being represented by a cylinder. Unstable information
+   bool cylindertype()
+    {
+      if (classification_type_ == CLASSIFICATION_CURVATURE)
+	return (group_points_[0]->C1_surf() == C1_RIDGE ||
+		group_points_[0]->C1_surf() == C1_VALLEY);
+      else
+	return false;
+    }
+    
+    /// Check if the regions is classified for being represented by a plane. Unstable information
+   bool planartype()
+    {
+      if (classification_type_ == CLASSIFICATION_CURVATURE)
+	return (group_points_[0]->C1_surf() == C1_FLAT);
+      else
+	return false;
+    }
+
+    /// Approximate region points with a plane if feasible, and register the plane
+    /// in the region. Possibly extract devinant points in connected groups
+    /// \param  mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param min_pt Not used
+    /// \param min_pt_reg Not used
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    /// \param out_groups Deviant points being removed from region
+    bool extractPlane(Point mainaxis[3],
+		      double tol, int min_pt, int min_pt_reg, double angtol,
+		      int prefer_elementary,
+		      std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+		      std::vector<HedgeSurface*>& prevsfs,
+		      std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+    /// Approximate region points with a cylinder if feasible, and register the cylinder
+    /// in the region. Possibly extract devinant points in connected groups
+    /// \param tol Approximation tolerance
+    /// \param min_pt Minimum number of points left after extracting deviant points to create
+    /// a surface
+    /// \param min_pt_reg Not used
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    /// \param out_groups Deviant points being removed from region
+    /// \param repeat Indicates that points are removed and another try to fit a cylinder is recommended
+    bool extractCylinder(double tol, int min_pt, int min_pt_reg, 
+			 double angtol, int prefer_elementary,
+			 std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			 std::vector<HedgeSurface*>& prevsfs,
+			 std::vector<std::vector<RevEngPoint*> >& out_groups,
+			 bool& repeat);
+
+    /// Check if the regions is suitable for being represented by a plane. Unstable information
+    bool feasiblePlane(double zero_H, double zero_K) const;
+
+    /// Check if the regions is suitable for being represented by a cylinder. Unstable information
+    bool feasibleCylinder(double zero_H, double zero_K) const;
+
+    /// Approximate region points with a sphere if feasible, and register the sphere
+    /// in the region. Possibly extract devinant points in connected groups
+    /// \param  mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param min_pt Not used
+    /// \param min_pt_reg Not used
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    /// \param out_groups Deviant points being removed from region
+     bool extractSphere(Point mainaxis[3],
+		      double tol, int min_pt, int min_pt_reg, 
+		       double angtol, int prefer_elementary,
+		       std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+		       std::vector<HedgeSurface*>& prevsfs,
+		       std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+    /// Approximate region points with a linear swept spline surface if feasible, and register 
+    /// the surface in the region. NB! Currently not in use
+    /// \param  mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param min_pt Not used
+    /// \param min_pt_reg Not used
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    bool extractLinearSweep(double tol, int min_pt, int min_pt_reg, 
+			    double angtol, int prefer_elementary,
+			    std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			    std::vector<HedgeSurface*>& prevsfs);
+
+    /// Approximate region points with a cone if feasible, and register the plane
+    /// in the region. Possibly extract devinant points in connected groups
+    /// \param tol Approximation tolerance
+    /// \param min_pt Not used
+    /// \param min_pt_reg Not used
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    /// \param out_groups Deviant points being removed from region
+    bool extractCone(double tol, int min_pt, int min_pt_reg, 
+		     double angtol, int prefer_elementary,
+		     std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+		     std::vector<HedgeSurface*>& prevsfs,
+		     std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+    /// Approximate region points with a torus if feasible, and register the torus
+    /// in the region. Possibly extract devinant points in connected groups
+    /// \param  mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param min_pt Not used
+    /// \param min_pt_reg Not used
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    /// \param out_groups Deviant points being removed from region
+   bool extractTorus(Point mainaxis[3],
+		      double tol, int min_pt, int min_pt_reg, 
+		      double angtol, int prefer_elementary,
+		      std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+		      std::vector<HedgeSurface*>& prevsfs,
+		      std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+    /// Approximate region points with a torus using context information if feasible, and 
+    /// register the torus in the region. Possibly extract devinant points in connected groups
+    /// \param  mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param min_pt Not used
+    /// \param min_pt_reg Minimum number of points left after extracting deviant points to create
+    /// a surface
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    /// \param out_groups Deviant points being removed from region
+    bool contextTorus(Point mainaxis[3],
+		      double tol, int min_pt, int min_pt_reg, 
+		      double angtol, int prefer_elementary,
+		      std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+		      std::vector<HedgeSurface*>& prevsfs,
+		      std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+    /// Approximate region points with a cylinder using context information if feasible, and 
+    /// register the cylinder in the region. Possibly extract devinant points in connected groups
+    /// \param  mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param min_pt Not used
+    /// \param min_pt_reg Minimum number of points left after extracting deviant points to create
+    /// a surface
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    /// \param out_groups Deviant points being removed from region
+    bool contextCylinder(Point mainaxis[3],
+			 double tol, int min_pt, int min_pt_reg,
+			 double angtol, int prefer_elementary,
+			 std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			 std::vector<HedgeSurface*>& prevsfs,
+			 std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+
+    /// Approximate region points with a sphere or a torus using context information from an
+    /// adjacent cylinder if feasible, and register the surface in the region. 
+    /// Possibly extract devinant points in connected groups
+    /// \param  mainaxis Local coordinate system for model
+    /// \param tol Approximation tolerance
+    /// \param min_pt Used to check if an approximation is accepted
+    /// \param min_pt_reg Not used
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    /// \param out_groups Deviant points being removed from region
+    bool adjacentToCylinder(Point mainaxis[3],
+			    double tol, int min_pt, int min_pt_reg,
+			    double angtol, int prefer_elementary,
+			    std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			    std::vector<HedgeSurface*>& prevsfs,
+			    std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+    /// Return closest region point to a given position along with the distance between
+    /// the points
+    RevEngPoint* closestPoint(const Point& pos, double& dist);
+
+    /// Return all region points that has neighbours belonging to a different region
+    std::vector<RevEngPoint*> extractBdPoints();
+
+    /// Return all region points that has neighbours belonging to a specified region
+    std::vector<RevEngPoint*> extractBdPoints(std::vector<RevEngRegion*> regions);
+    
+    /// Return all region points that has neighbours belonging to more than one region
+    /// different from current
+    std::vector<RevEngPoint*> extractBranchPoints();
+
+    /// Identify adjacent regions with and without an associated surface that are
+    /// candidates for being included in the current region
+    void getAdjCandMerge(std::vector<RevEngRegion*>& adj_surf,
+			 std::vector<RevEngRegion*>& adj_nosurf);
+
+    /// Estimate the length and width of a potential blend surface adjacent to this
+    /// region along the curves in cvs
+    void estimateBlendDimensions(std::vector<shared_ptr<CurveOnSurface> >& cvs,
+				 std::vector<RevEngPoint*>& bd_points,
+				 double tol, double distlim, 
+				 std::vector<std::pair<double,double> >& t1_t2,
+				 std::vector<double>& width, int& num_in_lim);
+
+    /// Extract points lying within a specified distance to a given curve where
+    /// the projection onto the curve falls within parameter interval <tmin, tmax>
+    void getNearPoints(shared_ptr<CurveOnSurface>& cvs,
+		       double& tmin, double& tmax, double width, double angtol,
+		       std::vector<RevEngPoint*>& nearpoints);
+
+    /// Extract points lying within a specified distance to a given set of curves
+    void getNearPoints2(std::vector<RevEngPoint*>& points,
+			shared_ptr<CurveOnSurface>& cv,
+			double width, std::vector<RevEngPoint*>& nearpoints);
+
+    /// Extract those points in points that satisfies given conditions with
+    /// respect to the region surface
+     std::vector<RevEngPoint*>
+    removeOutOfSurf(std::vector<RevEngPoint*>& points,
+		    double tol, double angtol, bool outer, double& min_dist);
+
+    /// Grow current region, which has a surface that is realistically bounded by
+    /// constant parameter curves (blend surface) with points from the region adjacent
+    /// if they satisfy the accuracy constraints
+    void growInDomain(RevEngRegion *adjacent, double tol, double angtol);
+
+    /// Check if a current surface can possibly be replaced by a surface with
+    /// better accuracy
+    bool tryOtherSurf(int prefer_elementary, bool replace);
+    
+    /// Approximate region points with a spline surface if feasible, and register 
+    /// the surface in the region. NB! Currently not in use
+    /// \param tol Approximation tolerance
+    /// \param min_pt Used in checking if the surface is feasible
+    /// \param min_pt_reg Not used
+    /// \param angtol Angular tolerance
+    /// \param prefer_elementary Used in checking if an already existing surface should be replaced
+    /// \param hedgesfs New surface
+    /// \param prevsfs Possibly previous surface being replaced
+    bool extractFreeform(double tol, int min_pt, int min_pt_reg,
+			 double angtol, int prefer_elementary,
+			 std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			 std::vector<HedgeSurface*>& prevsfs,
+			 std::vector<std::vector<RevEngPoint*> >& out_groups);
+
+    /// Divide composite region into more consistent pieces by extracting a planar piece
+    /// and requiring the remaining parts to be connected
+    void segmentByPlaneGrow(Point mainaxis[3], double tol,
+			    double angtol, int min_pt, 
+			    std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			    std::vector<HedgeSurface*>& prevsfs,
+			    std::vector<std::vector<RevEngPoint*> >& out_groups);
+    
+    /// Divide composite region into more consistent pieces sorting points with respect from
+    /// surface normals of adjacent planar surfaces. Regions with planar points are grown from
+    /// this region. Cylindrical surfaces may be recognized.
+    /// and requiring the remaining parts to be connected
+    bool segmentByPlaneAxis(Point mainaxis[3], int min_point_in,
+			    int min_pt_reg,
+			    double tol, double angtol, int prefer_elementary,
+			    std::vector<RevEngRegion*>& adj_planar,
+			    std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			    std::vector<shared_ptr<RevEngRegion> >& added_reg,
+			    std::vector<HedgeSurface*>& prevsfs,
+			    std::vector<std::vector<RevEngPoint*> >& added_groups);
+
+    /// Set pointer to associated surface. Remove possible previous surface
+    void setHedge(HedgeSurface* surface)
+    {
+      associated_sf_.clear();
+      associated_sf_.push_back(surface);
+      computeDomain();
+    }
+
+    /// Add an additional associated surface. Obsolete!
+    void addHedge(HedgeSurface* surface)
+    {
+      associated_sf_.push_back(surface);
+      computeDomain();
+    }
+
+    /// Enquire if a region is associated a surface
+    bool hasSurface()
+    {
+      return (associated_sf_.size() > 0);
+    }
+
+    /// Enquire the number of associated surfaces. Always one!
+    int numSurface()
+    {
+      return (int)associated_sf_.size();
+    }
+
+    /// Return specified surface
+    HedgeSurface* getSurface(int ix)
+    {
+      return (ix < 0 || ix>=(int)associated_sf_.size()) ? 0 : associated_sf_[ix];
+    }
+
+    /// Remove associated surface
+    void clearSurface()
+    {
+      if (associated_sf_.size() > 0)
+	associated_sf_.clear();
+      maxdist_ = avdist_ = 0.0;
+      num_inside_ = num_inside2_ = 0;
+    }
+
+    /// Compute accuracy and set associated surface
+    void setAssociatedSurface(shared_ptr<ParamSurface>& surf,
+			      double tol, double angtol, int min_pt_reg,
+			      shared_ptr<HedgeSurface>& hedge);
+
+    /// Modify region point collection. Used when the radius of a blend cylinder or
+    /// torus is modified
+    /// \param points_out Points to extract
+    /// \param points_in Points to include
+    void updateWithPointsInOut(std::vector<RevEngPoint*>& points_out,
+			       std::vector<RevEngPoint*>& points_in,
+			       double tol, double angtol);
+
+    /// Move points from points into blend_points if appropriate depending on
+    /// the distance to cvs and the flag in_blend
+    void sortBlendPoints(std::vector<RevEngPoint*>& points,
+			 std::vector<shared_ptr<CurveOnSurface> >& cvs,
+			 double distance, bool in_blend,
+			 std::vector<RevEngPoint*>& blend_points);
+    
+    /// Move points from points into this region or other region if appropriate
+    /// Points with a distance to cvs larger than distance are moved
+    void sortBlendPoints(std::vector<RevEngPoint*>& points,
+			 std::vector<shared_ptr<CurveOnSurface> >& cvs,
+			 double distance, RevEngRegion* other,
+			 std::vector<RevEngPoint*>& blend_points1,
+			 std::vector<RevEngPoint*>& blend_points2);
+
+    /// Enquire flag on surface accuracy
+    int getSurfaceFlag()
+    {
+      return surfflag_;
+    }
+
+    /// Set flag on surface accuracy
+    void setSurfaceFlag(int surfflag)
+    {
+      surfflag_ = surfflag;
+    }
+
+    /// Enquire information about the history of the associated surface. Information not
+    /// consistently carried out
+    int getAdaptionHistory()
+    {
+      return surf_adaption_;
+    }
+
+    /// Return containing domain of the parameter values of the region points in the
+    /// parameter domain of the associated surface.
+    void getDomain(double dom[4])
+    {
+      for (int ka=0; ka<4; ++ka)
+	dom[ka] = domain_[ka];
+    }
+
+
+    /// Set containing domain of parameter points
+    void setDomain(double dom[4])
+    {
+      for (int ka=0; ka<4; ++ka)
+	domain_[ka] = dom[ka];
+    }
+
+    /// Check if a fitted surface is accurate enough with respect to given tolerances,
+    /// number of region points and computed accuracy. Old function. Use defineSfFlag
+    bool accuracyOK(int min_pt, double tol, int num_inside, double avdist)
+    {
+      return (num_inside > min_pt && num_inside > (int)group_points_.size()/2 &&
+	      avdist <= tol);
+    }
+
+    /// Check if a fitted surface is accurate enough with respect to given tolerances
+    /// and computed accuracy. Old function. Use defineSfFlag
+    bool accuracyOK(int num_points, int min_pt, double tol, int num_inside,
+		    double avdist)
+    {
+      return (num_inside > min_pt && num_inside > num_points/2 &&
+	      avdist <= tol);
+    }
+
+    /// Check if a fitted surface is accurate enough with respect to given tolerances,
+    /// number of region points and computed accuracy.
+    /// \param min_point Function returns not set if the number of OK points is less than min_point
+    /// \param tol Approximation tolerance
+    /// \param num_in Number of points that satisfy the distance and angular tolerances
+    /// \param num_in2 Number of points that satisfy the distance tolerance
+    /// \param avd Average absolute distance between the points and the surface
+    /// \param type_cyl True if the surface is a cylinder or a cone
+    int defineSfFlag(int min_point, double tol, int num_in,
+		     int num_in2, double avd, bool type_cyl);
+    
+    /// Check if a fitted surface is accurate enough with respect to given tolerances,
+    /// given number of points and computed accuracy.
+    /// To be applied for a point group different from any region points
+    /// \param num_points Number of points tested for accuracy
+    /// \param min_point Function returns not set if the number of OK points is less than min_point
+    /// \param tol Approximation tolerance
+    /// \param num_in Number of points that satisfy the distance and angular tolerances
+    /// \param num_in2 Number of points that satisfy the distance tolerance
+    /// \param avd Average absolute distance between the points and the surface
+    /// \param type_cyl True if the surface is a cylinder or a cone
+    int defineSfFlag(int num_points, int min_point, double tol, int num_in,
+		     int num_in2, double avd, bool type_cyl);
+
+    /// Compute containing parameter domain of region points. Requires an associated surface
+    void computeDomain();
+
+    /// Bounding box for region points
+    const BoundingBox& boundingBox()
+    {
+      return bbox_;
+    }
+
+    /// Enquire direction cone for LocFuncnormal_ (normal based on local approximating function)
+    /// in the region points
+    const DirectionCone& getNormalCone()
+    {
+      return normalcone_;
+    }
+
+    /// Enquire direction cone for triangulation normal in the region points
+     const DirectionCone& getNormalConeTriang()
+    {
+      return normalcone2_;
+    }
+
+    /// Enquire range of principal curvatures in the region points
+   void getPrincipalCurvatureInfo(double& mink1, double& maxk1, double& mink2, double& maxk2)
+    {
+      mink1 = mink1_;
+      maxk1 = maxk1_;
+      mink2 = mink2_;
+      maxk2 = maxk2_;
+    }
+
+    /// Enquire average Gauss and mean curvatures in the region points
+    /// \param avH Average mean curvature
+    /// \param avK Average Gauss curvature
+    /// \param MAH Average absolue mean curvature
+    /// \param MAK Average absolute Gauss curvature
+   void getAvCurvatureInfo(double& avH, double& avK, double& MAH, double& MAK)
+    {
+      avH = avH_;
+      avK = avK_;
+      MAH = MAH_;
+      MAK = MAK_;
+    }
+
+    /// Enquire average LocFuncnormal_ (normal based on local approximating function)
+    Point getMeanNormal()
+    {
+      return avnorm_;
+    }
+
+    /// Enquire average triangle normal
+    Point getMeanNormalTriang()
+    {
+      return avnorm2_;
+    }
+    
+    /// Set accuracy information of region points
+    /// \param maxdist Maximum distance
+    /// \param avdist Average absolute distance
+    /// \param num_inside Number of points satisfying distance and angular tolerance,
+    /// \param num_inside2 Mumber of points satisfying distance tolerance
+    void setAccuracy(double maxdist, double avdist, int num_inside,
+		     int num_inside2);
+
+    /// Enquire accuracy information of region points
+    /// \param maxdist Maximum distance
+    /// \param avdist Average absolute distance
+    /// \param num_inside Number of points satisfying distance and angular tolerance,
+    /// \param num_inside2 Mumber of points satisfying distance tolerance
+    void getAccuracy(double& maxdist, double& avdist, int& num_inside,
+		     int& num_inside2)
+    {
+      maxdist = maxdist_;
+      avdist = avdist_;
+      num_inside = num_inside_;
+      num_inside2 = num_inside2_;
+    }
+
+    /// Enquire average absolute distance betweeb region points and surface
+    double getAverageDist()
+    {
+      return avdist_;
+    }
+
+    /// Enquire number of points satisfying distance and angular tolerance
+    int getNumInside()
+    {
+      return num_inside_;
+    }
+    
+    /// Enquire number of points satisfying distance tolerance
+    int getNumInside2()
+    {
+      return num_inside2_;
+    }
+
+    /// Enquire maximum distance between region points and surface
+    double getMaxSfDist()
+    {
+      return maxdist_;
+    }
+
+    /// Return distance to surface and angle between surface and point normal for all points
+    void getDistAndAng(std::vector<std::pair<double,double> >& distang);
+
+    /// In traversing points. Set as visited/not visited
+    void setVisited(bool visited)
+    {
+      visited_ = visited;
+    }
+
+    /// In traversing points. Check if visited
+    bool visited()
+    {
+      return visited_;
+    }
+
+    /// Check if the region is likely to correspond to a plane. Unstable information
+    bool possiblePlane(double angtol, double inlim);
+    
+    /// Check if the region is likely to correspond to a cylinder. Unstable information
+    bool possibleCylinder(double angtol, double inlim);
+    
+    /// Check if the region is likely to correspond to a cone. Unstable information
+    bool possibleCone(double angtol, double inlim);
+    
+    /// Check if the region is likely to correspond to a torus. Unstable information
+   bool possibleTorus(double angtol, double inlim);
+
+    /// Check if the regions contains information for defining a swept surface
+    bool hasSweepInfo()
+    {
+      return (sweep_.get() != 0);
+    }
+
+    /// Check if the regions contains information for defining a swept surface, and
+    /// whether it is linear or rotational (not implemented) sweep
+    int sweepType()
+    {
+      return (sweep_.get() ? sweep_->type_ : 0);
+    }
+
+    /// Enquire if the regions contains information on how to split this composite
+    /// region into consistent pieces
+    bool hasDivideInfo()
+    {
+      return (seg_info_.size() > 0);
+    }
+
+    /// Enquire number of information entities on how to split this composite
+    /// region into consistent pieces
+    int numDivideInfo()
+    {
+      return (int)seg_info_.size();
+    }
+
+    /// Define adjacency between this region and regions having points that are
+    /// neighbours to this regions points
+    void setRegionAdjacency();
+
+    /// Update information about neighbouring regions
+    void updateRegionAdjacency();
+
+    /// Include this region into an adjacent region if feasible
+    bool integrateInAdjacent(double mean_edge_len, int min_next,
+			     int max_next, double tol, double angtol,
+			     int max_nmb_outlier, RevEngRegion* taboo=0);
+
+    /// Change parameterization of associated surface of type plane and update
+    /// parameter values of region points
+    void setPlaneParam(int min_pt_reg, Point mainaxis[3], double tol, double angtol);
+
+    /// Given model coordinate system and axes from surfaces in adjacent region,
+    /// modify the surface in this region to adapt to the most feasible of the candidate
+    /// axes. Change surface if the accuracy of the new surface is sufficient
+    bool updateSurfaceWithAxis(int min_pt_reg, Point adj_axis,
+			       Point mainaxis[3], int ix, double tol,  
+			       double angtol, Point pos);
+
+    /// Add new adjacent region to the set of adjacent regions. NB! The adjacent region is not
+    /// updated with information about this region
+    void addAdjacentRegion(RevEngRegion* adj_reg)
+    {
+      //adj_reg->addAdjacentRegion(this);
+      adjacent_regions_.insert(adj_reg);
+    }
+
+    /// Remove adjacent region from the set of adjacent regions. NB! The adjacent region is not
+    /// updated 
+    void removeAdjacentRegion(RevEngRegion* adj_reg)
+    {
+      //adj_reg->removeAdjacentRegion(this);
+      if (std::find(adjacent_regions_.begin(), adjacent_regions_.end(), adj_reg) != adjacent_regions_.end())
+	adjacent_regions_.erase(adj_reg);
+      // else
+      // 	std::cout <<"Something wrong in adjacent regions" << std::endl;
+    }
+
+    /// Remove all information about region adjacency. NB! adjacent regions are not updated
+    void clearRegionAdjacency()
+    {
+      adjacent_regions_.clear();
+    }
+
+    /// Check if this region is adjacent to adj_reg
+    bool isAdjacent(RevEngRegion* adj_reg)
+    {
+      return (adjacent_regions_.find(adj_reg) != adjacent_regions_.end());
+    }
+
+    /// Check if this region is adjacent to a regions that is adjacent to adj_reg
+    bool isNextToAdjacent(RevEngRegion* adj_reg)
+    {
+      for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+	{
+	  bool adjacent = (*it)->isAdjacent(adj_reg);
+	  if (adjacent)
+	    return true;
+	}
+      return false;
+    }
+
+    /// Fetch all regions adjacent to this region and adj_reg 
+    std::vector<RevEngRegion*> commonAdjacent(RevEngRegion* adj_reg);
+
+    /// Enquire number of adjacent regions
+    int numAdjacentRegions()
+    {
+      return (int)adjacent_regions_.size();
+    }
+
+    /// Fetch all adjacent regions
+    void getAdjacentRegions(std::vector<RevEngRegion*>& adjacent)
+    {
+      adjacent.insert(adjacent.end(), adjacent_regions_.begin(), adjacent_regions_.end());
+    }
+
+    /// Remove current regions from the adjacency information of all adjacent regions
+     void removeFromAdjacent()
+    {
+      for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+	(*it)->removeAdjacentRegion(this);
+    }
+
+    /// Collect adjacent regions with and associated planar surface
+    std::vector<RevEngRegion*> fetchAdjacentPlanar();
+
+    /// Collect adjacent regions with and associated cylindrical surface
+    std::vector<RevEngRegion*> fetchAdjacentCylindrical();
+
+    /// Divide composite region into simpler pieces using information from
+    /// adjacent regions
+    bool segmentByAdjSfContext(Point mainaxis[3], int min_point_in, 
+			       int min_pt_reg, double tol, double angtol,
+			       std::vector<RevEngRegion*>& adj_planar,
+			       std::vector<std::vector<RevEngPoint*> >& added_groups);
+
+    /// Sort region points into groups with point normal being close to parallel or
+    /// close to orthogonal to a set of axes (axis) and a group of remaining points
+    bool sortByAxis(vector<Point>& axis, double tol, double axisang, double planeang,
+		    std::vector<std::vector<RevEngPoint*> >& groups1,
+		    std::vector<std::vector<RevEngPoint*> >& groups2,
+		    std::vector<RevEngPoint*>& remaining);
+
+    /// Identify cylindrical sub groups in a composite region and define cylindrical
+    /// surfaces. The remaining points are split into connected groups
+    bool extractCylByAxis(Point mainaxis[3], int min_point, int min_pt_reg,
+			  double tol, double angtol, int prefer_elementary,
+			  std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			  std::vector<shared_ptr<RevEngRegion> >& added_reg,
+			  std::vector<std::vector<RevEngPoint*> >& out_groups,
+			  std::vector<RevEngPoint*>& single_pts);
+
+    /// Use segmentation information (SegmentData) to split composite region
+    bool divideWithSegInfo(int seg_ix, int min_pt_reg,
+			   std::vector<std::vector<RevEngPoint*> >& sep_groups,
+			   std::vector<RevEngPoint*>& single_pts);
+
+    /// Return surfaces axis/surface normal from the adjacent region with surface and
+    /// most region points
+    Point directionFromAdjacent(double angtol);
+
+    /// Divide composite region using direction from adjacent and split region points
+    /// according to the relation of their point normal to this direction
+    bool segmentByDirectionContext(int min_point_in, double tol,
+				   const Point& direction, double angtol,
+				   std::vector<std::vector<RevEngPoint*> >& added_groups);
+
+    /// Check if context information (surfaces in adjacent regions) indicate that this
+    /// region could belong to a blend surface
+    bool potentialBlend(double angtol);
+
+    /// Fetch adjacent regions to this region that has a blend relation to cvs
+    void neighbourBlends(std::vector<shared_ptr<CurveOnSurface> >& cvs,
+			 double width, double tol,
+			 std::vector<RevEngRegion*>& new_blends);
+
+    /// Fetch adjacent regions with primary surfaces or primary base surfaces and surfaces
+    void getAdjacentElemInfo(std::vector<std::pair<shared_ptr<ElementarySurface>, RevEngRegion*>  >& adj_elem,
+			     std::vector<std::pair<shared_ptr<ElementarySurface>, RevEngRegion*>  >& adj_elem_base);
+
+    /// Flag the region with information about the region from which it was extracted
+    void setPreviousReg(RevEngRegion *prev)
+    {
+      prev_region_ = prev;
+    }
+
+    /// Extend the pool of trimming curves in this region with curves identifying
+    /// boundaries between this region and adjacent regions with no surface or other
+    /// regions where no boundaries between this region and the other region is defined
+    void extendBoundaries(double mean_edge_len, int min_point_reg, double tol, 
+			  double angtol, Point mainaxis[3]);
+    
+
+    /// Check if the region has a base surface defined. Partly displaced concept
+    bool hasBaseSf()
+    {
+      return basesf_.get();
+    }
+
+    /// Fetch base surface. Used in the context of defining a model coordinate system
+    shared_ptr<ParamSurface> getBase()
+    {
+      return basesf_;
+    }
+    
+     /// Fetch base surface including accuracy information. 
+    void getBase(shared_ptr<ParamSurface>& base, double& maxd, double& avd,
+		 int& num_in, int& num_in2)
+    {
+      base = basesf_;
+      maxd = maxdist_base_;
+      avd = avdist_base_;
+      num_in = num_in_base_;
+      num_in2 = num_in_base2_;
+    }
+    
+     /// Fetch accuracy information related to base surface. 
+    void getBaseDist(double& maxd, double& avd, int& num_in, int& num_in2)
+    {
+      maxd = maxdist_base_;
+      avd = avdist_base_;
+      num_in = num_in_base_;
+      num_in2 = num_in_base2_;
+    }
+
+    /// Update region with surface modified to adapt to a defined model axis
+    /// and set related parameterization and accuracy information
+    void updateSurfaceAndInfo(shared_ptr<ParamSurface> surf,
+			      double tol, double angtol,
+			      std::vector<double>& parvals,
+			      std::vector<std::pair<double,double> >& dist_ang,
+			      std::vector<RevEngEdge*>& nopar_edgs);
+
+    /// Compute the approximation accuracy of this region with respect to a number of
+    /// primary surfaces. Select the best and return accuracy information
+    int checkSurfaceAccuracy(std::vector<shared_ptr<ElementarySurface> >& sfs, double tol,
+			     double angtol, double& maxd, double& avd, int& num_in,
+			     int& num2_in, int& sf_flag);
+
+    /// Parameterize region points with respect to associated surface and update parameterization
+    /// and accuracy information in points and region
+    void parameterizePoints(double tol, double angtol);
+    
+    /// For each curve in cvs, fetch information on the part of the curve lying in the
+    /// vicinity of the region points
+    bool getCurveRestriction(std::vector<shared_ptr<CurveOnSurface> >& cvs,
+			     double tol, double anglim,
+			     std::vector<std::pair<double, double> >& endpars);
+
+    /// Mark that this region should be associated to a future blend surface represented
+    /// by blend_edge
+    void setAssociatedBlend(RevEngEdge* blend_edge)
+    {
+      associated_blend_ = blend_edge;
+    }
+
+    /// Check if this region is associated with a blend
+    bool hasAssociatedBlend()
+    {
+      return (associated_blend_ != 0);
+    }
+
+    /// Fetch the edge representing the blend with which this region is associated
+    RevEngEdge* getAssociatedBlend()
+    {
+      return associated_blend_;
+    }
+
+    /// Remove the edge representing a blend from this region
+    void removeAssociatedBlend()
+    {
+      associated_blend_ = 0;
+    }
+
+    /// Check if this region is adjacent to any (future) blend surfaces
+    bool hasRevEdges()
+    {
+      return (rev_edges_.size() > 0);
+    }
+
+    /// Check the number of (future) blend surfaces associated to this region
+    int numRevEdges()
+    {
+      return (int)rev_edges_.size();
+    }
+
+    /// Fetch a given edge representing a (future) blend surface
+    RevEngEdge* getRevEdge(int ix)
+    {
+      if (ix < 0 || ix >= (int)rev_edges_.size())
+	return 0;
+      else
+	return rev_edges_[ix];
+    }
+
+    /// Fetch all edges representing (future) blend surfaces
+    std::vector<RevEngEdge*> getAllRevEdges()
+    {
+      return rev_edges_;
+    }
+
+    /// Extend the collection of edges representing (future) blend surfaces
+    void addRevEdge(RevEngEdge* edge)
+    {
+      rev_edges_.push_back(edge);
+    }
+
+    /// Remove edge from the collection of edges representing (future) blend surfaces
+    void removeRevEngEdge(RevEngEdge *edg)
+    {
+      auto it = std::find(rev_edges_.begin(), rev_edges_.end(), edg);
+      if (it != rev_edges_.end())
+	rev_edges_.erase(it);
+    }
+
+    /// Check if this region and the region other meets in a (future) blend surface
+    bool commonRevEdge(RevEngRegion *other);
+    
+    /// Check if this region and the region other are separated by a trimming curve
+    /// with twins
+    bool commonTrimEdge(RevEngRegion *other);
+
+    /// Mark that is region corresponds to a blend surface and set pointer to the edge
+    /// representing this blend surface
+    void setBlendEdge(RevEngEdge* edge)
+    {
+      blend_edge_ = edge;
+    }
+
+    /// Check if this region corresponds to a blend surface
+    bool hasBlendEdge()
+    {
+      return (blend_edge_ != 0);
+    }
+
+    /// Fetch the edge containing information on e.g. the surfaces between which
+    /// the surface associated to this region is a blend
+    RevEngEdge* getBlendEdge()
+    {
+      return blend_edge_;
+    }
+
+    /// Remove blend information from this region
+    void removeBlendEdge()
+    {
+      blend_edge_ = 0;
+    }
+
+    /// Increase the collection of trimming curves in this region with edge
+    void addTrimEdge(shared_ptr<ftEdge> edge)
+    {
+      trim_edgs_.push_back(edge);
+    }
+
+    /// Check if this region has any trimming information
+    bool hasTrimEdges()
+    {
+      return (trim_edgs_.size() > 0);
+    }
+
+    /// Fetch the number of trimming curve associated to this region
+    int numTrimEdges()
+    {
+      return (int)trim_edgs_.size();
+    }
+
+    /// Fetch all trimming curves associated to this region
+    std::vector<shared_ptr<ftEdge> > getTrimEdges()
+    {
+      return trim_edgs_;
+    }
+
+    /// Restrict the size of associated RevEngEdges with respect to the
+    /// extent of the region points
+    void adaptEdges();
+
+    /// Mark this region to be removed from the region pool in Reveng
+    void setRemove()
+    {
+      to_be_removed_ = true;
+    }
+
+    /// Check if this region is to be removed from the region pool in Reveng
+    bool toBeRemoved()
+    {
+      return to_be_removed_;
+    }
+
+    /// Trim associated surface with respect to the trimming curves of this region,
+    /// and replace the surface with a BoundedSurface if feasible
+    bool trimSurface(double tol);
+
+
+    /// Include the points of the region reg into this region, update parameterization
+    /// according to parvals and accuracy information according to maxd, avd, num_inside
+    /// num_inside2 and dist_ang, and update region adjacency information
+    void includeAdjacentRegion(RevEngRegion* reg, double maxd, double avd,
+			       int num_inside, int num_inside2,
+			       std::vector<double>& parvals,
+			       std::vector<std::pair<double, double> >& dist_ang,
+			       std::vector<RevEngRegion*>& added_adjacent);
+
+    /// Check if the current region surface should be replaced by a blend surface
+    void checkEdgeAssociation(double tol, int min_point_reg,
+			      std::vector<HedgeSurface*>& removed_sfs);
+
+    /// Write debug information to file
+    void writeRegionInfo(std::ostream& of);
+    void writeRegionPoints(std::ostream& of);
+    void writeAdjacentPoints(std::ostream& of);
+    void writeUnitSphereInfo(std::ostream& of);
+    void writeSubTriangulation(std::ostream& of);
+    void writeSurface(std::ostream& of);
+
+    /// Store current stage of region to file
+    void store(std::ostream& os) const;
+
+    /// Read region information from file
+    void read(std::istream& is, shared_ptr<ftPointSet>& tri_sf,
+	      std::vector<int>& associated_sf_id);
+	      
+  private:
+    /// Unique id for region. Used in storing and reading data structure to and from file
+    int Id_;
+
+    /// Triangle vertices with additional information collected into this region
+    /// Initially points belonging to classified segment. Updated during reverse engineering workflow
+    std::vector<RevEngPoint*> group_points_;
+
+    /// Selected method for classification in work flow
+    int classification_type_;
+    int edge_class_type_;
+
+    /// Surface associated to this region. Only one surface is stored
+    std::vector<HedgeSurface*> associated_sf_;
+
+    /// Flag representing the accuracy with which the associated surface approximates the region
+    /// points
+    int surfflag_;
+
+    /// History of surface approximation. Information not  consistently carried out
+    int surf_adaption_;
+
+    /// Rectangular domain surrounding the parameter pairs corresponding to the region points
+    /// and the associated surface
+    double domain_[4];
+
+    /// Range of principal curvatures for region points
+    double mink1_, maxk1_, mink2_, maxk2_;
+
+    /// Average curvature in points: avH_ = mean, avK_ = Gauss, MAH_ = absolute valu of mean,
+    /// MAK_ = absolute value of Gauss
+    double avH_, avK_, MAH_, MAK_;
+
+    /// Bounding box for region points
+    BoundingBox bbox_;
+
+    /// Direction cone for LocFunc normal in region points
+    DirectionCone normalcone_;
+    
+    /// Direction cone for triangulation normal in region points
+    DirectionCone normalcone2_;
+
+    /// Fraction of LocFunc normal in points being closes to the average LocFunc normal
+    /// than the given angular tolerance
+    double frac_norm_in_;
+    
+    /// Fraction of triangulation normal in points being closes to the average triangulation normal
+    /// than the given angular tolerance
+    double frac_norm_in2_;
+
+    /// Average LocFunc normal
+    Point avnorm_;
+
+    /// Average triangulation normal
+    Point avnorm2_;
+
+    /// Maximum and average absolute value of distance between region points and surface
+    double maxdist_, avdist_;
+    
+    /// Number of points satisfying distance and angular tolerance and number of points
+    /// satisfyng the distance tolerance
+    int num_inside_, num_inside2_;
+
+    /// Regions adjacent to this one
+    std::set<RevEngRegion*> adjacent_regions_;
+
+    /// Set if the region is extracted from a previous region to avoid it being integrated
+    /// in the same region again
+    RevEngRegion* prev_region_;
+
+    /// Alternative approximating surface with accuracy information
+    /// Can be set if the surface is modified due to adaption to model axes
+    shared_ptr<ParamSurface> basesf_;
+    double maxdist_base_, avdist_base_;
+    int num_in_base_, num_in_base2_;
+
+    /// Associated edges representing (future) blend surfaces between this region surface
+    /// and another region surface
+    std::vector<RevEngEdge*> rev_edges_;
+
+    /// The region points is associated blend surface represented by this edge
+    RevEngEdge* associated_blend_;
+
+    /// The surface of this region is a blend surface associated to blend_edge
+    RevEngEdge* blend_edge_;
+
+    /// Collection of trimming edges intended for eventually bounding the region surface
+    std::vector<shared_ptr<ftEdge> > trim_edgs_;
+
+    /// Information that can be used to fit this regions points with a swept spline surface
+    shared_ptr<SweepData> sweep_;
+
+    /// Indicates that this region is visited in a search
+    bool visited_;
+
+    /// Indicates if this region is marked to be removed from the region pool in RevEng
+    bool to_be_removed_;
+
+    /// Information suitable for dividing this region that is found to be composite into
+    /// simpler regions
+    std::vector<shared_ptr<SegmentData> > seg_info_;;
+    
+    struct grow_cand
+    {
+      RevEngRegion *cand_;
+      double maxd_, avd_, avang_;
+      int num_in_, num2_in_, ang_in_;
+
+      grow_cand(RevEngRegion* cand, double maxd, double avd, double avang,
+		int num_in, int num2_in, int ang_in)
+      {
+	cand_ = cand;
+	maxd_ = maxd;
+	avd_ = avd;
+	avang_ = avang;
+	num_in_ = num_in;
+	num2_in_ = num2_in;
+	ang_in_ = ang_in;
+      }
+    };
+
+    void integrateGrowCand(std::vector<grow_cand>& cand,
+			   Point mainaxis[3], double tol,
+			   double angtol, std::vector<RevEngRegion*>& grown_regions,
+			   std::vector<HedgeSurface*>& adj_surfs);
+    
+    void analyseNormals(double tol, Point& normal, Point& centre, double& radius);
+    void analysePlaneProperties(Point avnorm, double angtol,
+				std::vector<RevEngPoint*>& in,
+				std::vector<RevEngPoint*> out);
+    void analyseCylinderProperties(Point avvec, double angtol,
+				   std::vector<RevEngPoint*>& in,
+				   std::vector<RevEngPoint*> out);
+    void configSplit(std::vector<RevEngPoint*>& points,
+		     std::vector<double>& param,
+		     shared_ptr<Cylinder> cyl,
+		     shared_ptr<SplineCurve> spl, double tol,
+		     std::vector<std::vector<RevEngPoint*> >& configs);
+    shared_ptr<Plane> computePlane(std::vector<RevEngPoint*>& points,
+				   const Point& norm_dir, Point mainaxis[3]);
+    shared_ptr<Cylinder>
+    computeCylinder(std::vector<RevEngPoint*>& points, double tol);
+    void analyseCylProject(shared_ptr<Cylinder> cyl, double tol,
+			   std::vector<std::vector<RevEngPoint*> >& configs);
+    void analyseCylRotate(shared_ptr<Cylinder> cyl, double tol,
+			  double avdist, int num_in, double& avdist_lin,
+			  int& num_in_lin, double& avdist_cub,
+			  int& num_in_cub, shared_ptr<Cone>& cone);
+
+    shared_ptr<Sphere> computeSphere(Point mainaxis[3], Point adj_axis,
+				     std::vector<RevEngPoint*>& points);
+    shared_ptr<Cone> computeCone(std::vector<RevEngPoint*>& points, Point& apex);
+    shared_ptr<Torus> computeTorus(std::vector<RevEngPoint*>& points,
+				   std::vector<Point>& adj_axis,
+				   double tol, double angtol);
+    shared_ptr<SplineSurface> computeLinearSwept(double tol, shared_ptr<SplineCurve>& profile,
+						 Point& pt1, Point& pt2);
+    shared_ptr<SplineSurface> computeFreeform(std::vector<RevEngPoint*>& points,
+					      double tol);
+    shared_ptr<SplineSurface> updateFreeform(std::vector<RevEngPoint*>& points,
+					     double tol);
+    void getPCA(double lambda[3], Point& eigen1, Point& eigen2, Point& eigen3);
+    void getPCA(std::vector<RevEngPoint*>& points,
+		double lambda[3], Point& eigen1, Point& eigen2, Point& eigen3);
+    shared_ptr<SplineSurface> surfApprox(vector<RevEngPoint*>& points,
+					 const BoundingBox& bbox);
+    void splitCylinderRad(const Point& pos, const Point& axis,
+			  const Point& Cx, const Point& Cy,
+			  int nmb_split, std::vector<Point>& centr,
+			  std::vector<double>& rad);
+    void approximationAccuracy(std::vector<RevEngPoint*>& points,
+			       shared_ptr<ParamSurface> surf,
+			       double tol, double angtol,
+			       double& maxd, double& avd,
+			       std::vector<RevEngPoint*>& in,
+			       std::vector<RevEngPoint*>& out);
+    bool parameterizeOnSurf(std::vector<RevEngPoint*>& points,
+			    shared_ptr<ParamSurface> surf,
+			    std::vector<double>& data,
+			    std::vector<double>& param,
+			    int& inner1, int& inner2, bool& close1, bool& close2);
+   bool parameterizeOnSurf(shared_ptr<ParamSurface> surf,
+			    std::vector<double>& data,
+			    std::vector<double>& param,
+			    int& inner1, int& inner2, bool& close1, bool& close2);
+    bool reparameterize(std::vector<RevEngPoint*>& points,
+			std::vector<double>& param, std::vector<double>& param2,
+			double& umin, double& umax, double& vmin, double& vmax);
+
+    bool reparameterize(std::vector<double>& param, std::vector<double>& param2,
+			double& umin, double& umax, double& vmin, double& vmax);
+
+    void getParExtent(double curr[2], int pdir, std::vector<std::vector<int> >& raster,
+		      int& i1, int& i2);
+
+    void defineRaster(std::vector<double>& param, int nmb_div,
+		      std::vector<std::vector<int> >& raster, double& umin,
+		      double& umax, double& vmin, double& vmax);
+
+    void extendInCorner(std::vector<double>& data, std::vector<double>& param,
+			double umin, double umax, double vmin, double vmax);
+
+    bool computeIntegrateInfo(std::vector<RevEngPoint*>& points, RevEngRegion *adj_reg,
+			      double tol, double angtol, double radius, bool local_approx, 
+			      int min_next, int max_next, int max_nmb_outlier, 
+			      bool& outlier, int& nmb_pt_adj, double& maxdist, 
+			      double& avdist, int& nmb_in, double& maxdist_adj, 
+			      double& avdist_adj, int& nmb_in_adj,
+			      int& nmb_in_adj2);
+
+        bool
+    analyseTorusContext(std::vector<std::pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj,
+			double tol, double angtol, std::vector<size_t>& adjacent_ix,
+			int& plane_ix, int& cyl_ix, Point& pos, Point& axis,
+			Point& Cx, double& R1, double& R2, double cyl_dom[4],
+			bool& outer, bool& analyse_rotated);
+    bool
+    analyseCylinderContext(std::vector<std::pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj,
+			   double tol, double angtol, Point mainaxis[3], int mode,
+			   std::vector<std::pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj_planar,
+			   Point& pos, Point& axis, Point& Cx, double& rad,
+			   std::vector<RevEngPoint*>& cyl_pts);
+
+    void computeFracNorm(double angtol, Point mainaxis[3], int nmb_axis[3],
+			 double& in_frac1, double& in_frac2);
+    
+    shared_ptr<ElementarySurface>
+    helicalComponent(shared_ptr<Cylinder> cyl,  double tol,
+		     double angtol, int min_point,
+		     int min_pt_reg, double avdist, int num_in2,
+		     std::vector<std::pair<double,double> >& dist_ang,
+		     std::vector<RevEngPoint*>& remaining,
+		     std::vector<std::vector<RevEngPoint*> >& extracted,
+		     double& maxd_out, double& avd_out,
+		     int& num_in_out, int& num2_in_out,
+		     std::vector<double>& parvals_out,
+		     std::vector<std::pair<double,double> >& distang_out);
+
+    bool defineHelicalInfo(shared_ptr<Cylinder> cyl,  double tol,
+			   double angtol, int min_point, int min_pt_reg,
+			   double avdist, int num_in1, int num_in2,
+			   std::vector<std::pair<double,double> >& dist_ang,
+			   std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			   std::vector<std::vector<RevEngPoint*> >& out_groups,
+			   std::vector<RevEngPoint*>& single_pts);
+    
+    bool defineConeFromCyl(shared_ptr<Cylinder> cyl, double tol,
+			   double angtol, int min_pt_reg,
+			   double avdist, int num_in, int num2_in,
+			   std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			   std::vector<std::vector<RevEngPoint*> >& out_groups,
+			   std::vector<RevEngPoint*>& single_pts);
+    
+    bool integratePlanarPoints(std::vector<Point>& dir,
+			       std::vector<std::vector<RevEngPoint*> >& groups,
+			       std::vector<std::pair<shared_ptr<ElementarySurface>,RevEngRegion*> >& adj_elem,
+			       double tol, double angtol,
+			       std::vector<RevEngPoint*>& remaining);
+    
+    bool defineCylindricalRegs(Point mainaxis[3],
+			       std::vector<std::vector<RevEngPoint*> >& groups,
+			       int min_point, int min_pt_reg,
+			       double tol, double angtol,
+			       std::vector<shared_ptr<RevEngRegion> >& added_reg,
+			       std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			       std::vector<RevEngPoint*>& remaining);
+    
+    void axisFromAdjacent(double angtol, std::vector<Point>& axis);
+    
+    void identifyOutPoints(std::vector<std::pair<double,double> >& distang,
+			   double tol, double angtol, double angtol2,
+			   std::vector<vector<RevEngPoint*> >& out_groups,
+			   std::vector<RevEngPoint*>& remaining);
+
+    void getRemainingPoints(std::vector<RevEngPoint*>& curr_pts,
+			    std::vector<RevEngPoint*>& remaining);
+
+    void adaptOneEdge(shared_ptr<ftEdge>& edge, double dom[4]);
+
+    bool arrangeEdgeLoop(double tol, std::vector<int>& adjusted);
+    
+    shared_ptr<CurveOnSurface>
+    constParSfCv(shared_ptr<ParamSurface> surf, int dir,
+		 double par, int bd, double t1, double t2);
+
+    void getDomainBoundaries(double tol, double angtol,
+			     std::vector<std::pair<int, double> >& bd_par1,
+			     std::vector<std::pair<int, double> >& bd_par2);
+    
+    void blendGrowFromAdjacent(RevEngRegion* adjacent,
+			       std::vector<int>& pt_ix, double tol,
+			       double angtol,
+			       std::vector<RevEngPoint*>& grow_pt);
+    
+    // Select a point appropriate for starting the search for a sub group of points
+    // that can be approximated by a plane
+    RevEngPoint* seedPointPlane(int min_next, double rfac, double angtol);
+
+    void updateRegion(double approx_tol, double anglim,
+		      std::vector<RevEngRegion*>& adapted_regions,
+		      std::vector<shared_ptr<RevEngRegion> >& outdiv_regions);
+
+     void identifyAngPoints(std::vector<std::pair<double, double> >& dist_ang,
+			    double tol, double disttol,
+			    std::vector<RevEngPoint*>& ang_points);
+    
+     void identifyAngPoints(std::vector<std::pair<double, double> >& dist_ang,
+			    double tol, double dtol, double dtol2,
+			    std::vector<RevEngPoint*>& ang_points,
+			    std::vector<RevEngPoint*>& remaining);
+    
+    void removeOtherPoints(std::vector<RevEngPoint*>& keep,
+			   std::vector<HedgeSurface*>& prevsfs,
+			   std::vector<std::vector<RevEngPoint*> >& out_groups);
+    
+    bool contextCylinder(Point mainaxis[3],
+			 double tol, int min_pt, int min_pt_reg, 
+			 double angtol, int prefer_elementary,
+			 std::vector<std::pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj_elem,
+			 std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			 std::vector<HedgeSurface*>& prevsfs,
+			 std::vector<std::vector<RevEngPoint*> >& out_groups,
+			 int mode=1);
+
+    RevEngPoint* closestParPoint(const Point& parpt, double& dist);
+
+    std::vector<RevEngPoint*> extractNextToAdjacent(RevEngRegion* reg);
+
+    void extractPointsAtSeam(std::vector<RevEngPoint*>& seam_pts1,
+			     std::vector<RevEngPoint*>& seam_pts2, bool along_udir);
+    
+   void testBlendGap(std::vector<shared_ptr<CurveOnSurface> >& cvs,
+		      double tmin, double tmax, double tdel, double width,
+		      std::vector<std::pair<double,double> >& not_gap);
+
+    void growFromNeighbour(Point mainaxis[3], int min_pt_reg,
+			   std::vector<RevEngPoint*>& seed, double tol,
+			   double angtol, RevEngRegion *neighbour,
+			   bool do_update=true);
+
+    shared_ptr<ParamSurface> surfaceWithAxis(std::vector<RevEngPoint*>& points,
+					     Point axis, Point pos,
+					     Point mainaxis[3]);
+
+    bool adjustWithCylinder(Point mainaxis[3],
+			    double tol, double angtol, int min_pt_reg,
+			    std::vector<std::vector<RevEngPoint*> >& out_groups,
+			    std::vector<RevEngRegion*>& grown_regions,
+			    std::vector<HedgeSurface*>& adj_surfs);
+    
+    void getAdjInsideDist(shared_ptr<ParamSurface> surf, double dom[4],
+			  double tol, RevEngRegion* reg,
+			  double& avd, double& ava, int& nn,
+			  std::vector<RevEngPoint*>& adjpts,
+			  std::vector<double>& par_and_dist);
+
+    bool identifySignificantAxis(std::vector<std::pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj,
+				 Point& pos, Point& axis, Point& axis2);
+
+    void analyseRotated(Point& pos, Point& axis, Point& axis2);
+    
+    shared_ptr<Torus>
+    analyseRotatedTorus(Point& pos, Point& Cx, Point& normal,
+			double tol, double angtol);
+    
+    bool isInBlend(std::vector<shared_ptr<CurveOnSurface> >& cvs,
+		   double width, double tol, int& in_blend);
+
+    std::vector<RevEngPoint*> sortPtsSeq(double mean_edge_len,
+					 std::vector<RevEngPoint*>& seq_pts,
+					 std::vector<RevEngPoint*>& sub_pts);
+
+    vector<RevEngPoint*>  extractBdOutPoints(shared_ptr<SplineCurve>& crv,
+					     std::vector<RevEngPoint*>& seq_pts,
+					     double tol);
+
+    void adjustWithSurf(Point mainaxis[3], int min_pt_reg, double tol, double angtol);
+
+    double getFracNormTriang()
+    {
+      return frac_norm_in2_;
+    }
+    
+    void setBaseSf(shared_ptr<ParamSurface> base, double maxd, double avd,
+		   int num_in, int num_in2)
+    {
+      basesf_ = base;
+      maxdist_base_ = maxd;
+      avdist_base_ = avd;
+      num_in_base_ = num_in;
+      num_in_base2_ = num_in2;
+    }
+
+    // Check if this region and the region adj both is connected to a triangulation
+    // vertex (not in any region) that is defined as an edge point
+    bool hasEdgeBetween(RevEngRegion* adj);
+
+    void checkReplaceSurf(Point mainaxis[3], int min_pt_reg, double tol,
+			  double angtol, bool always=false);
+
+    void computeSurface(std::vector<RevEngPoint*>& points,
+			Point mainaxis[3], double tol, double angtol,
+			ClassType classtype, shared_ptr<ParamSurface>& updated,
+			shared_ptr<ParamSurface>& updated2, bool& cyllike);
+    
+    void  curveApprox(std::vector<Point>& points, double tol,
+		      shared_ptr<Circle> circle,
+		      std::vector<double>& param,
+		      shared_ptr<SplineCurve>& curve, Point& xpos);
+
+    void getAdjacentBlends(std::vector<RevEngRegion*>& adj_blends);
+    
+    void rangeAlongAxis(const Point& pos, const Point& axis, double& tmin,
+			double& tmax);
+    
+    shared_ptr<Plane>
+    planarComponent(Point vec, int min_point, int min_pt_reg,
+		    double tol, double angtol, Point mainaxis[3],
+		    std::vector<RevEngPoint*>& remaining,
+		    std::vector<std::vector<RevEngPoint*> >& extracted);
+    
+   bool planarComponent(Point vec, int min_point, int min_pt_reg, double tol,
+			 double angtol, Point mainaxis[3],
+			 std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			 std::vector<vector<RevEngPoint*> >& out_groups,
+			 std::vector<RevEngPoint*>& single_pts,
+			 bool create_surface = true);
+
+};
+}
+
+#endif
diff --git a/compositemodel/include/GoTools/compositemodel/RevEngUtils.h b/compositemodel/include/GoTools/compositemodel/RevEngUtils.h
new file mode 100644
index 00000000..5dcd1a66
--- /dev/null
+++ b/compositemodel/include/GoTools/compositemodel/RevEngUtils.h
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#ifndef _REVENGUTILS_H
+#define _REVENGUTILS_H
+
+#include "GoTools/utils/Point.h"
+#include "GoTools/compositemodel/RevEngPoint.h"
+#include "GoTools/geometry/ParamSurface.h"
+#include "GoTools/geometry/SplineSurface.h"
+#include "GoTools/geometry/SplineCurve.h"
+#include "GoTools/utils/BoundingBox.h"
+#include <vector>
+
+namespace Go {
+
+  class RevEngPoint;
+  class Line;
+  class Plane;
+  class Cylinder;
+  class Torus;
+  class Sphere;
+  class Cone;
+  
+  /** RevEngUtils -  Utility functionality for everse engineering.
+   * 
+   */
+  
+  namespace RevEngUtils
+  {
+    /// Principal analysis based on a group of points
+    void principalAnalysis(Point& curr, std::vector<Point>& points, 
+			   double lambda[3], double eigenvec[3][3]);
+
+    void principalAnalysis(std::vector<RevEngPoint*>& points, 
+			   double lambda[3], double eigenvec[3][3]);
+
+    /// Rotate point group according to specified coordinate system (x=vec1, y=vec2),
+    /// and approximate points with a bicubic function. Compute surface normal and
+    /// principal curvatures
+    void computeLocFunc(Point& curr, std::vector<Point>& points,
+			Point& vec1, Point& vec2, Point& normal, Point& mincvec,
+			double& minc, Point& maxcvec, double& maxc,
+			double& currdist, double& avdist);
+
+    /// Interface to the class SmoothSurf
+    void smoothSurf(shared_ptr<SplineSurface>& surf, int fixed);
+
+    /// Approximate parameterized data points given information of polynomial degrees
+    /// and initial number of coefficients. Iterate until the tolerance tol or the
+    /// maximum number of iterations (max_iter) is reached. Parameter iteration is performed.
+    /// Output of accuracy (maximum distance, average distance and number of points outside
+    /// the tolerance) and final parameter pairs associated to the points
+    shared_ptr<SplineSurface> surfApprox(std::vector<double>& data, int dim,
+					 std::vector<double>& param, int order1,
+					 int order2, int nmb_coef1, int nmb_coef2,
+					 bool close1, bool close2,
+					 int max_iter, double tol, double& maxd, 
+					 double& avd, int& num_out,
+					 std::vector<double>& parvals,
+					 double del=0.0);
+    
+    /// Surface approximation given parameterized data points and information to create
+    /// spline space. Limits of parameter intervals is given by the parameterized points
+    shared_ptr<SplineSurface> surfApprox(std::vector<double>& data, int dim,
+					 std::vector<double>& param, int order1,
+					 int order2, int nmb_coef1, int nmb_coef2,
+					 double del=0.0);
+
+    /// Surface approximation given parameterized data points and information to create
+    /// spline space. Limits of parameter intervals is given as input
+    shared_ptr<SplineSurface> surfApprox(std::vector<double>& data, int dim,
+					 std::vector<double>& param, int order1,
+					 int order2, int nmb_coef1, int nmb_coef2,
+					 double umin, double umax, double vmin,
+					 double vmax);
+
+    /// Paramerize RevEngPoints by projecting them onto a given plane (spanned by vec1 and
+    /// vec2). Extracted xyz values in data and parameters (uv) in param
+    void parameterizeWithPlane(std::vector<RevEngPoint*>& pnts, const BoundingBox& bbox,
+			       const Point& vec1, const Point& vec2,
+			       std::vector<double>& data, std::vector<double>& param);
+    
+    /// Paramerize points by projecting them onto a given plane (spanned by vec1 and
+    /// vec2). xyz values in data and parameters (uv) in param
+    void parameterizeWithPlane(std::vector<Point>& pnts, const BoundingBox& bbox,
+			       const Point& vec1, const Point& vec2,
+			       std::vector<double>& data, std::vector<double>& param);
+
+    /// Parameterize RevEngPoints on the surface surf. Returns information to specify
+    /// a spline space in which the points can be approximated (inner1, inner2, close1, close2)
+    bool parameterizeOnPrimary(std::vector<RevEngPoint*>& points,
+			       shared_ptr<ParamSurface> surf,
+			       std::vector<double>& data, 
+			       std::vector<double>& param,
+			       int& inner1, int& inner2, bool& close1, bool& close2);
+
+    /// Cylinder axis and x- and y-vectors from a point group
+    void computeAxis(std::vector<Point>& points,
+		     Point& axis, Point& Cx, Point& Cy);
+
+    /// Cylinder axis and x- and y-vectors from a number of RevEngPoint groups
+    void computeAxis(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+		     std::vector<RevEngPoint*>::iterator> >& points,
+		     Point& axis, Point& Cx, Point& Cy);
+
+    /// Sphere centre and radius from a number of RevEngPoint groups
+    void
+    computeSphereProp(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+		      std::vector<RevEngPoint*>::iterator> >& points,
+		      Point& centre, double& radius);
+
+    /// Cone axis and x- and y-vectors from a number of RevEngPoint groups. If the
+    /// point group corresponds to a small cone sector, the function for computing
+    /// a cylinder axis may be more appropriate
+    void coneAxis(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+		     std::vector<RevEngPoint*>::iterator> >& points,
+		     Point& axis, Point& Cx, Point& Cy);
+
+    /// Compute cone apex and opening angle
+    void coneApex(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+		  std::vector<RevEngPoint*>::iterator> >& points,
+		  Point axis, Point& apex, double& phi);
+
+    /// Compute cylinder position and radius given point and axis
+    void computeCylPosRadius(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+			     std::vector<RevEngPoint*>::iterator> >& points,
+			     Point& low, Point& high, Point& axis, Point& Cx, 
+			     Point& Cy, Point& pos, double& radius);
+
+    /// Given 3D points in a plane given with normal and x- and y-axis, compute
+    /// circle center and radius
+    void computeCircPosRadius(std::vector<Point>& points,
+			      const Point& axis, const Point& Cx, const Point& Cy,
+			      Point& pos, double& radius);
+    
+    /// As above, given 3D points in a plane given with normal and x- and y-axis, compute
+    /// radius
+    void computeRadius(std::vector<Point>& points,
+		       Point& axis, Point& Cx, Point& Cy, double& radius);
+
+    /// Compute line approximating an unordered group of points
+    void computeLine(vector<Point>& points, Point& pos, Point& dir);
+    
+    /// Approximate a group of points with a plane  using implicit approximation.
+    /// An initial surface normal guess must be provided. Returns point in plane and
+    /// surface normal
+    void computePlane(std::vector<Point>& points, Point normal, Point mainaxis[3],
+		      Point& pos, Point& norm, Point& Cx, Point& Cy);
+
+    /// Rotate a number of RevEngPoint groups around the axis axis with point mid and 
+    /// return the resulting points in the plane spanned by xvec and axis
+    void rotateToPlane(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+		       std::vector<RevEngPoint*>::iterator> >& points,
+		       Point& xvec, Point& axis, Point& mid, std::vector<Point>& rotated);
+
+    /// Project a number of RevEngPoint groups into the plane represented by the point mid and the
+    /// normal axis and return the resulting points along with information about the distance
+    /// between the initial points and the plane
+    void projectToPlane(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+			std::vector<RevEngPoint*>::iterator> >& points,
+			Point& axis, Point& mid, std::vector<Point>& projected,
+			double& maxdist, double& avdist, double dlen=-1.0);
+
+    /// Project a number of RevEngPoints into the plane represented by the point mid and the
+    /// normal axis and return the resulting points along with information about the distance
+    /// between the initial points and the plane
+    void projectToPlane(std::vector<RevEngPoint*>& points,
+			Point& axis, Point& mid, std::vector<Point>& projected,
+			double& maxdist, double& avdist, double dlen=-1.0);
+
+    /// Rotate points around the axis axis width point mid and return the resulting
+    /// points in the plane spanned by xvec and axis
+     void rotateToPlane(std::vector<Point>& points,
+			Point& xvec, Point& axis, Point& mid, std::vector<Point>& rotated);
+
+    /// Compute distance between RevEngPoints and a surface, and summarize results
+    /// \param start Start iterator to vector of points
+    /// \param end Iterator to end of vector
+    /// \param surf Surface with whom the points will be compared
+    /// \param tol Approximation tolerance
+    /// \param maxdist Maximum distance between the point cloud and the surface
+    /// \param avdist Average absolute distance between the point cloud and the surface
+    /// \param inside Number of points where the distance is less than tol and the angle
+    /// between the surface normal and the normal in the point (least of triangle normal and
+    /// local function normal) is less than angtol if given
+    /// \param inside2 Number of points where the distance is less than tol
+    /// \param in Points being inside
+    /// \param out Points not being inside
+    /// \param parvals Parameter values of points projected onto the surface (u1, v1, u2, v2, ...)
+    /// \param distang Distance and angle difference between normal vectors for each point
+   void distToSurf(std::vector<RevEngPoint*>::iterator start,
+		    std::vector<RevEngPoint*>::iterator end,
+		    shared_ptr<ParamSurface> surf, double tol,
+		   double& maxdist, double& avdist,
+		   int& inside, int& inside2,
+		   std::vector<RevEngPoint*>& in,
+		   std::vector<RevEngPoint*>& out,
+		   std::vector<double>& parvals,
+		   std::vector<std::pair<double,double> >& distang,
+		   double angtol=-1.0);
+
+    /// As above. Slightly simplified interface
+   void distToSurf(std::vector<RevEngPoint*>::iterator start,
+		   std::vector<RevEngPoint*>::iterator end,
+		   shared_ptr<ParamSurface> surf, double tol,
+		   double& maxdist, double& avdist,
+		   int& inside, int& inside2,
+		   std::vector<double>& parvals,
+		   std::vector<std::pair<double,double> >& distang,
+		   double angtol=-1.0);
+
+    /// As above. Simplified interface
+    void distToSurf(std::vector<RevEngPoint*>& points,
+		    shared_ptr<ParamSurface> surf, double tol,
+		    double angtol, double& maxdist, double& avdist, 
+		    int& inside, int& inside2,
+		    std::vector<std::pair<double,double> >& dist_ang);
+    
+    /// As above. Taking points as input. Simplified interface. Only distance
+    /// between points and surface is considered (normals not available)
+    void distToSurf(std::vector<Point>& points,
+		    shared_ptr<ParamSurface> surf, double tol,
+		    double& maxdist, double& avdist, int& inside,
+		    std::vector<double>& distance);
+
+    /// Distance between points and curve.
+    /// \param points Input points
+    /// \param curve Check distance to the curve
+    /// \param tol Approximation tolerance
+    /// \param maxdist Maximum distance between the point cloud and the curve
+    /// \param avdist Average absolute distance between the point cloud and the curve
+    /// \param inside Number of points where the distance is less than tol 
+    /// \param parvals Parameter values of points projected onto the curve (t1, t2, ...)
+    /// \param dist Distance for each point
+    void distToCurve(std::vector<Point>& points,
+		     shared_ptr<ParamCurve> curve, double tol,
+		     double& maxdist, double& avdist, int& inside,
+		     std::vector<double>& parvals, std::vector<double>& dist);
+    
+    /// Distance between points and curve. Slightly simplified intervace
+     void distToCurve(std::vector<Point>& points,
+		     shared_ptr<ParamCurve> curve, double tol,
+		     double& maxdist, double& avdist, int& inside,
+		     std::vector<double>& dist);
+    
+
+    /// Distance between points and curve. Simplified intervace
+    void distToCurve(std::vector<Point>& points,
+		     shared_ptr<ParamCurve> curve, double tol,
+		     double& maxdist, double& avdist, int& inside);
+
+    /// Modify given primary surface (sf_in) to fit in the coordinate system given
+    /// by mainaxis
+    /// \param points Point group associated with the surface
+    /// \diag Used to bound the surface
+    shared_ptr<ElementarySurface> elemsurfWithAxis(shared_ptr<ElementarySurface> sf_in,
+						 std::vector<RevEngPoint*>& points,
+						   Point mainaxis[3], double diag);
+    
+    /// Compute plane with surface normal axis to best fit the point group points
+    /// \param init_loc Initial point in plane
+    /// \param mainaxis Used to define the plane parameterization
+   shared_ptr<Plane> planeWithAxis(std::vector<RevEngPoint*>& points,
+				    Point axis, Point init_loc,
+				    Point mainaxis[3]);
+    
+    /// Compute cylinder approximating the RevEngPoints points. The cylinder axis
+    /// axis is input. mainaxis is used to compute x- and y-vectors. The points low and high
+    /// are used to assist locating the cylinder point
+    shared_ptr<Cylinder> cylinderWithAxis(std::vector<RevEngPoint*>& points,
+					  Point axis, Point low, 
+					  Point high, Point mainaxis[3]);
+    
+    /// Compute cylinder approximating the RevEngPoints points. The cylinder axis,
+    /// x-vector and point is given as input. The cylinder radius is computed
+    shared_ptr<Cylinder> cylinderWithAxis(std::vector<RevEngPoint*>& points,
+					  Point axis, Point Cx, Point pos);
+    
+    /// Compute torus approximating the RevEngPoints points. The torus axis,
+    /// and mid point is given as input. Minor and major radius is computed. mainaxix
+    /// is used to define x- and y-vector
+    shared_ptr<Torus> torusWithAxis(std::vector<RevEngPoint*>& points,
+				    Point axis, Point loc, 
+				    Point mainaxis[3]);
+    
+    /// Compute torus approximating the RevEngPoints points. The torus axis,
+    /// x-vector and mid point is given as input. Minor and major radius is computed. 
+    shared_ptr<Torus> torusWithAxis(std::vector<RevEngPoint*>& points,
+				    Point axis, Point Cx, Point pos);
+    
+    /// Approximate sphere from points. The sphere axis is given as input and the remaining
+    /// coordinate axes are computed from mainaxis
+    shared_ptr<Sphere> sphereWithAxis(std::vector<RevEngPoint*>& points,
+				      Point axis, 
+				      Point mainaxis[3]);
+
+    /// Compute sphere radius from points. Center and coordinate axes are given
+    shared_ptr<Sphere> sphereWithAxis(std::vector<RevEngPoint*>& points,
+				      Point axis, Point Cx, Point pos);
+    
+    /// Compute cone approximating the RevEngPoints points. The cone axis
+    /// axis is input. mainaxis is used to compute x- and y-vectors. The points low and high
+    /// are used to assist locating a point on the axis
+     shared_ptr<Cone> coneWithAxis(vector<RevEngPoint*>& points,
+				  Point axis, Point low, 
+				  Point high, Point mainaxis[3]);
+    
+    /// Compute cone approximating the RevEngPoints points. The  axis,
+    /// x-vector and point on axis is given as input. The cone radius at the point and
+    /// the cone opening angle is computed
+    shared_ptr<Cone> coneWithAxis(std::vector<RevEngPoint*>& points,
+				  Point axis, Point Cx, Point pos,
+				  double len);
+
+    /// Compute plane approximating a number of RevEngPoint groups (points)
+    /// \param bbox BoundingBox containing all points
+    /// \param nmbpts Number of points for each point group
+    /// \param set_bound Whether the plane should be bounded (from bbox information)
+    shared_ptr<ParamSurface> doMergePlanes(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+					   std::vector<RevEngPoint*>::iterator> > points,
+					   const BoundingBox& bbox,
+					   std::vector<int>& nmbpts,
+					   bool set_bound = true);
+    /// Compute cylinder approximating a number of RevEngPoint groups (points)
+    /// Parameters as for doMergePlanes
+    shared_ptr<ParamSurface> doMergeCylinders(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+					      std::vector<RevEngPoint*>::iterator> > points,
+					      const BoundingBox& bbox,
+					      std::vector<int>& nmbpts,
+					      bool set_bound = true);
+    /// Compute sphere approximating a number of RevEngPoint groups (points)
+    /// Parameters as for doMergePlanes
+    /// \param normal Axis through sphere poles. The sphere  is initially bounded
+     shared_ptr<ParamSurface> doMergeSpheres(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+					    std::vector<RevEngPoint*>::iterator> > points,
+					    const BoundingBox& bbox,
+					    std::vector<int>& nmbpts, Point& normal);
+    
+    /// Compute torus approximating a number of RevEngPoint groups (points)
+    /// Parameters as for doMergePlanes. The torus is initially bounded
+     shared_ptr<ParamSurface> doMergeTorus(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+					  std::vector<RevEngPoint*>::iterator> > points,
+					  const BoundingBox& bbox,
+					  std::vector<int>& nmbpts);
+
+    /// Middle curve between two spline curves
+    shared_ptr<SplineCurve> midCurve(shared_ptr<SplineCurve>& cv1,
+				     shared_ptr<SplineCurve>& cv2);
+
+    /// Approximate points with spline curve. The points are parameterized on the curve
+    /// cvin. ik=degree+1, in=number of coefficients
+    void  curveApprox(std::vector<Point>& points,
+		      shared_ptr<ParamCurve> cvin,
+		      int ik, int in, 
+		      shared_ptr<SplineCurve>& curve);
+    
+    /// Approximate parameterized points with spline curve. ik=degree+1, in=number of coefficients
+    void  curveApprox(std::vector<Point>& points,
+		      std::vector<double>& param,
+		      int ik, int in, 
+		      shared_ptr<SplineCurve>& curve);
+
+    /// Approximate an ordered sequence of RevEngPoints by a spline curve with degree degree
+    /// Refinement of the intial spline space and reparameterization is performed at most maxiter
+    /// times
+    /// \param tol Approximation tolerance
+    shared_ptr<SplineCurve> createCurve(std::vector<RevEngPoint*>& points, int degree,
+					double tol, int maxiter);
+
+    /// Extract points at the start or end of a point sequence where corresponding
+    /// points rotated into a given plane (input) can be approximated by a line.
+    /// Special function accessed from RevEngRegion
+    void extractLinearPoints(std::vector<RevEngPoint*>& points, 
+			     std::vector<Point>& rotated, double len,
+			     Point& pos, Point& axis, double rad,
+			     Point& axis2, bool plane,
+			     double tol, double angtol,
+			     std::vector<std::pair<double,double> >& dist_ang,
+			     std::vector<RevEngPoint*>& linear, bool start,
+			     std::vector<RevEngPoint*>& remaining);
+
+    bool extractLinearPoints(std::vector<Point>& points,
+			     shared_ptr<Line>& line, Point norm, double tol, 
+			     bool start, double& splitpar,
+			     std::vector<Point>& linear, 
+			     std::vector<Point>& remaining);
+    
+    void identifyEndPoints(std::vector<RevEngPoint*> edge_pts, shared_ptr<CurveOnSurface>& sfcv,
+			   RevEngPoint*& first_pt, double& t1, RevEngPoint*& last_pt, double& t2);
+
+    /// Reorder curves to form a sequence. The parameter direction of curves may be changed
+    void setLoopSeq(std::vector<shared_ptr<CurveOnSurface> >& cvs);
+
+    /// Identifyed groups of connected RevEngPoints (by triangle edges) in a given point group
+    void identifyConGroups(std::vector<RevEngPoint*>& init,
+			   std::vector<std::vector<RevEngPoint*> >& groups);
+  }
+  
+} // namespace Go
+
+
+#endif // _REVENGUTILS_H
+
diff --git a/compositemodel/include/GoTools/compositemodel/compositemodel-doxymain.h b/compositemodel/include/GoTools/compositemodel/compositemodel-doxymain.h
index e0b5df19..d1365cf1 100644
--- a/compositemodel/include/GoTools/compositemodel/compositemodel-doxymain.h
+++ b/compositemodel/include/GoTools/compositemodel/compositemodel-doxymain.h
@@ -237,5 +237,9 @@ size of the model. An upper bound of the tesselation size exists, but a large
 model combined with a small density will still lead to a heavy visualization
 model.
 
+\section comp_sec3 Reverse engineering
+The aim is to go from a triangulated point cloud to a boundary represented
+CAD model. See \link reverseengineering_doc the reverse engineering page\endlink 
+for more information.
 */
 #endif // _COMPOSITEMODEL_DOXYMAIN_H
diff --git a/compositemodel/include/GoTools/compositemodel/reverseengineering_doxymain.h b/compositemodel/include/GoTools/compositemodel/reverseengineering_doxymain.h
new file mode 100644
index 00000000..f79432d0
--- /dev/null
+++ b/compositemodel/include/GoTools/compositemodel/reverseengineering_doxymain.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#ifndef _REVERSEENGINEERING_DOXYMAIN_H
+#define _REVERSEENGINEERING_DOXYMAIN_H
+
+/**
+\page reverseengineering_doc Reverse engineering, from triangulated point cloud to CAD model.
+
+The defined workflow constitues an early prototype for reverse engineering. The 
+functionality is expected to be applied to mechanical objects. The mathematical 
+description of these types of objects is characterized by predominantly use of
+primary surfaces, e.g. planes, cylinders, cones, spheres and torii. Free form
+surfaces are used mostly for blends and small details. The extent of sharp edges in
+the  objects is small. Edges are in general blended. Relevant objects are often
+made of casted iron or created by adaptive manufacturing giving rough surfaces. 
+Only the part of the surface that are critical for assembly is plastered, leaving
+most of the part with small irregularities. Simple models and the main surfaces 
+of more complex models is expected to be reconstructed.
+
+\section re_sec1 Interface
+The reverse engineering engine is the class
+\link Go::RevEng RevEng\endlink. The class is instanciated with a triangulated surface 
+represented as an \link Go::ftPointSet ftPointSet\endlink, which represents a triangulation
+in GoTools. Assume that the triangulation
+is represented as a collection of vertices and triangles in the format
+\verbatim
+  vector<double> vertices;
+  vector<int> triangles;
+\endverbatim
+The points are stored consequetive in vertices as (x,y,z) while three indices in triangles
+represents one triangle. The triangles are expected to have a consistent orientation. The
+vertices and triangles are transferred to ftPointSet with the following code snippet:
+\verbatim
+  size_t nvertices = vertices.size()/3;
+  size_t ntriangles = triangles.size()/3;
+  shared_ptr<ftPointSet> tri_sf = shared_ptr<ftPointSet>(new ftPointSet());
+  for (size_t ki=0; ki<nvertices; ++ki)
+    {
+      Vector3D xyz(vertices[3*ki], vertices[3*ki+1], vertices[3*ki+2]);
+      shared_ptr<RevEngPoint> currpt(new RevEngPoint(xyz, -1));
+      currpt->setIndex(ki);
+      tri_sf->addEntry(currpt);
+    }
+
+  for (size_t ki=0; ki<ntriangles; ++ki)
+    {
+      ftSamplePoint* pt1 = (*tri_sf)[triangles[3*ki]];
+      ftSamplePoint* pt2 = (*tri_sf)[triangles[3*ki+1]];
+      ftSamplePoint* pt3 = (*tri_sf)[triangles[3*ki+2]];
+      pt1->addTriangle(pt2, pt3);
+      pt2->addTriangle(pt3, pt1);
+      pt3->addTriangle(pt1, pt2);
+    }
+\endverbatim
+\link Go::RevEngPoint RevEngPoint\endlink represents a triangulation vertex and is inherited
+from \link Go::ftSamplePoint ftSamplePoint\endlink. RevEngPoint is enhanced with information
+such as estimated surface normal and curvature as well as associated functionality.
+
+\section re_sec2 Overview
+The reverse engineering process is organized as a sequence of operations that together 
+consistute a work flow. The process is as follows:
+
+ * <ol>
+ * <li> Enhance points
+ * <li> Classify points according to Gauss and mean curvature
+ * <li> Segment point cloud into regions
+ * <li> Surface creation
+ * <li> Compute global properties such as main axes and update surfaces accordingly
+ * <li> Edge creation
+ * <li> Define blend surfaces
+ * <li> Trim surfaces with respect to identified edges, blend surfaces and adjacent regions
+ * <li> Create CAD model 
+
+Point 4 to 6 are repeated three times, each time differently. The process
+is automated, but organized as a sequence of commands to RevEng. This allows for storing
+the state at a number of locations to resume the computation at a convenient time. Note that
+storing and reading the state can be time consuming. In the following, we will describe
+the process in some detail. The figure below shows a triangulated surface with
+400 thousand vertices. This data set will be used to illustrate the reverse
+engineering process.
+
+\image html Tarn_init.gif "Triangulated surface"  width=600px 
+
+\section re_sec3 Workflow
+The first function to call is RevEng::enhancePoints. The points are approximated by a surface
+in a local neighbourhood. Surface normal and principal curvature estimates are 
+computed from this surface. Approximation errors are registered and used to set an
+approximation tolerance for the proceeding computations. An additional surface normal is
+computed from the triangulation. The two versions of the surface normal have different
+pros and cons, and both are used in the computations.
+
+Classification is performed in RevEng::classifyPoints. It is based on the size and
+sign of estimated Gauss and mean curvature in the points. Very small curvature values are
+deciphered as zero. A small curvature radius compared to the average distance between
+triangle vertices indicates that the point is a part of an edge. The expected typical 
+measured objects has rounded edges. Thus, edge detection not a prioritized topic in the current version of the 
+functionality. As the initial triangulation may lack smoothness, the curvature
+information is somewhat unstable, but still appropriate for recognizing significant 
+regions suitable for being represented by one surface. In the image below, pink colour 
+indicates that the area around a point is classified as flat, green and orange points
+have Gauss curvature close to zero while the remaining colours indicate some 
+curvature.
+
+\image html Tarn_classify.gif "Vertices coloured according to curvature properties" width=600px
+
+Next, the approximation tolerance is set by the call 
+RevEng::setApproximationTolerance based on information from the preceeding computations. 
+Alternatively, the application can use the
+function RevEng::setApproxTol(double tol) if more control is preferred. As the given
+point cloud is expected to be noisy, it is only required that a majority points associated 
+to a surface will be fit by the surface within this tolerance. There are also requirements on the average approximation error and surface normal direction.
+
+RevEng::segmentIntoRegions collects connected groups of points with the same classification.
+Identified edge points are excluded. This is a two-stage process. First connected points
+with the same classification are grouped, then small groups are merged with their
+neighbour if feasible. Each group is stored in an instance of \link Go::RevEngRegion RevEngRegion\endlink. In the image below, the individual groups of some size are given 
+separate colours. Very small groups are shown in black. 
+
+\image html Tarn_segment.gif "Segmented point cloud" width=800px
+
+The first surface creation is performed in RevEng::initialSurfaces. Regions with a 
+significant number of points are selected and tentatively fitted by a plane, 
+a cylinder or a cone. As the point classification can be misleading several
+attempts are made and the best fit is selected if it satisfies the accuracy
+requirements. Simultanously, points that are found to belong to other surfaces are
+dismissed from the region. For our test example, only regions with more than 1357
+points are considered for surface creation. This number is estimated from the 
+current region sizes. The surfaces are represented as 
+\link Go::ParamSurface ParamSurface\endlink and embedded in 
+\link Go::Hedgeurface HedgeSurface\endlink, which is inherited from
+\link Go::ftEdge ftEdge\endlink. The collection of HedgeSurfaces are owned by 
+the RevEng entity, but each surface is linked to the associated RevEngRegion. 
+The result of the first surface creation for our test case is shown in the image below.
+
+\image html Tarn_initsurf.gif "The first surfaces, associationed points and main regions" width=800px
+
+The model is still incomplete. Some, even major, surfaces are missing. Transition zones
+are missing and the surfaces don't have any matching directions. Another region growing is
+performed in RevEng::growSurfaces, this time with the existence of some surfaces where
+the distance between the points of a region and the surface of the adjacent region
+can be measured. Next an identification of similar plane normals and rotational surface
+axis is performed in RevEng::updateAxesAndSurfaces. Surfaces with almost identical 
+axes are updated to be consistent. If this implies a major detoriation of the
+surface accuracy, the update is dismissed. A coordinate system for the model is
+defined.
+
+The first edge recognition is performed in RevEng::firstEdges. Adjacent and almost
+adjacent surfaces are intersected and the intersection curve is stored in 
+\link Go::RevEngEdge RevEngEdge\endlink along with nearby regions. These regions is 
+associated with the blend surface for whom the edge is a place holder. The edge
+also contains some context information. In the figure below, we see that some 
+edges are still missing and that the edges don't join up. The main cylindrical surface
+and the middle plane meets in five different edges. One is split at the seam of the
+cylinder.
+
+\image html Tarn_edges1.gif "The first edges and associationed points" width=600px
+
+The first sequence of point 4 to 6 in the process overview is completed. Now 
+RevEng::surfaceCreation is applied to continue the surface recognition. At this stage,
+it is also possible to recognize spheres and torii. As in the first surface
+recognition pass, more than one primary surface can be fitted to the points, and
+the best fit is choosen if accurate enough. Some regions may be composed by several sub
+groups of points that can be associated one surface. The identified model
+coordinate system provides a tool to split these regions into consistent parts.
+Adjacent surfaces with the same characteristics are merged and the region structure
+is simplified further by including small regions into adjacent ones when possible.
+The figure below shows the 16 surfaces identified in the example model and the
+regions associated to these surfaces.
+
+\image html Tarn_surfaces2.gif "Updated surface structure and associated regions" width=600px
+
+RevEng::adaptToMainAxis updates the axis information obtained by updateAxesAndSurfaces
+and complements information on axis direction with axis position. After harmonazing
+the surfaces with respect to updated global information, possible missing edges are
+computed.
+
+At this stage the major surfaces and associated candidate blends are expected to
+be indentified but there may still be unidentified smaller surfaces. Curvature  
+information and point classification become unstable close to edges in the model and
+the restriction on number of points to initiate surface fitting lead to a high
+possibility of unrecognized surfaces. In RevEng::smallRegionSurfaces adjacent region 
+fragments are collected with the aid of global information. Such merged regions are
+fitted with planes, cylinders and cones if applicable. The region structure may
+be updated by merging adjacent regions into those associated with the new surfaces and
+by merging surfaces, and associated regions, representing the same feature. Additional
+edges may be defined.
+
+RevEng::manageBlends1 computed blend surface associated to the collected RevEngEdges.
+The blend surfaces will either be cylinders or torii and some coordination of blend
+radii is performed. Depending on the actual configuration of the adjacent surfaces,
+some gaps or overlaps may occur. The current state for our example model is
+visualized in the figure below, first all surfaces
+including blends, then the large cylinder is removed for better visibility of the
+blend surfaces and finally a detail is emphasized.
+
+\image html Tarn_blend1.gif "Surfaces with edge blends" width=800px
+
+RevEng::manageBlends2 bounds the blend surfaces in the blend direction and defines
+corresponding trimming curves represented as \link Go::ftEdge ftEdge\endlink and
+stored in the blend region. The
+geometry representation of the trimming curves are stored in an instance of
+\link Go::CurveOnSurface CurveOnSurface\endlink having
+curves both in geometry and parameter space. The corresponding curves in the
+surfaces being blended are also computed.
+
+Edge blend surfaces will often meet in a corner blend. Configurations resulting in 
+torus corners and some 4-sided free form (spline) corners are supported. Additional
+trimming curves are added to the associated regions. The figure below shows, in the
+first picture, all surfaces including blends and corner blends. In the following
+pictures some surfaces are removed for visibility.
+
+\image html Tarn_blend2.gif "Surfaces with edge and corner blends" width=800px
+
+RevEng::trimSurfaces arranges the identified trimming curves for each surface in
+loops and compute bounded surfaces. The figures below illustrate the procedure for
+two surfaces in our example case. The points associated to the main cylinder surface
+is shown to the left in the first figure along with trimming curves in geometry space.
+The curves are extended beyond their real size and must be reduced. This computation
+is performed in parameter space. Curves along the cylinder seam is added to the 
+collection. Then the trimming curves are cut at intersections with the adjacent 
+curves and arranged in a loop. The parameter curves before and after this modification
+are shown in the middle two pictues. To the right, the final trimmed surface and the
+modified trimming curves in geometry space are shown.
+
+\image html Tarn_trim1.gif "Trimming of cylinder surface" width=800px
+\image html Tarn_trim2.gif "Trimming of plane" width=800px
+
+The trimming of the middle planar surface is shown in the figure above. The procedure
+is similar to the cylinder case, but in this case we have an internal trimming curve.
+This curve is computed as a boundary towards point regions without an associated
+surface. The initial triangular surface lacks information in this area, thus the
+expected cylindrical surface inside the model is not found. The shape of this
+trimming curve is inferior to the ones found by the intersect and blend 
+procedure and in newer versions it is dismissed during the trimming operation.
+
+
+
+\image html Tarn_surface_collection.gif "All trimmed surfaces" width=600px
+
+The final surface collection is shown in the image above. RevEng::createModel
+finalizes the work flow. Adjacency analysis is performed using functionality in
+\link Go::SurfaceModel SurfaceModel\endlink and \ref topology. The final model can be
+exported in a \link Go::CompositeModelFileHandler g22-file\endlink.
+
+\section re_sec4 Workflow summary - implementation
+The workflow described above is implemented through a number of calls to functions in
+the reverse engineering workflow RevEng. The function calls are listed below, and all 
+must be executed.
+- RevEng::Reveng(shared_ptr<ftPointSet> triangulation)
+- RevEng::enhancePoints() *
+- RevEng::classifyPoints() * 
+- RevEng::setApproxTolerance() / RevEng::setApproxTol(double tolerance)
+- RevEng::segmentIntoRegions() *
+- RevEng::initialSurfaces() *
+- RevEng::growSurfaces() *
+- RevEng::updateAxesAndSurfaces() *
+- RevEng::firstEdges() *
+- RevEng::surfaceCreation() *
+- RevEng::adaptToMainAxis() *
+- RevEng::smallRegionSurfaces() * 
+- RevEng::manageBlends1() *
+- RevEng::manageBlends2()
+- RevEng::trimSurfaces()
+- shared_ptr<SurfaceModel> RevEng::createModel()
+
+The workflow is automatic, the only possible current interaction by the application is
+to set the tolerance. However, more user interaction is expected to be preferable.
+Then user interaction can be used to perform quality control the type of surfaces recognized and
+to enable regularization of the model with respect to for instance parallelity, 
+orthogonality and symmetry. This type of interference can in future versions of the
+work flow be included between the calls to RevEng functions.
+
+The workflow can be stopped and started at a number of stages. The possibilities
+are marked with a star in the list above. To stop the execution after for instance
+initialSurfaces and restart at a later stage. To store the required information,
+the procedure is:
+\verbatim
+  RevEng reveng;
+  std::ofstream of("initial_surface_stage.txt");
+  of << SURFACEONE << std::endl;
+  reveng.storeGrownRegions(of);
+\endverbatim
+
+To restart:
+\verbatim
+  std::ifstream is(char *data_file);
+  int stage;
+  is >> stage;
+  reveng.readGrownRegions(is);
+  if (stage <= SURFACEONE)
+    {
+      reveng.growSurfaces();
+
+      // Continue with the remaining functions
+    }
+\endverbatim
+
+Suggested flags for the stages are:
+\verbatim 
+  enum
+    {
+     ENHANCED, CLASSIFIED, SEGMENTED, SURFACEONE, GROWONE, UPDATEONE, EDGESONE, SURFACETWO, UPDATETWO, SURFACETHREE, BLENDONE
+    };
+\endverbatim
+Note that storing and reading the execution stage may be time consuming.
+
+Debug information can be fetched from RevEng after the definition of regions, that
+means after executing segmentIntoRegions(), by calling
+\verbatim
+  void writeRegionStage(std::ostream& of, std::ostream& ofm, std::ostream& ofs) const;
+  void writeRegionWithSurf(std::ostream& of) const;
+  void writeEdgeStage(std::ostream& of) const;
+\endverbatim
+writeRegionStage will write points and surfaces organized in regions to three files.
+The points of regions with a significant number of points are written to "of" 
+together with possible associated surfaces. Regions with less points are written to 
+"ofm" and all points from regions with few points are collected and written to "ofs".
+writeRegionWithSurf outputs only those regions that has associated points.
+writeEdgeStage writes all RevEngEdges and point groups associated to expected blend
+surfaces. The debug information can be visualized in \ref viewlib.
+
+
+\section re_sec5 Data structure
+The reverse engineering functionality is integrated in GoTools. The main engine is
+\link Go::RevEng RevEng\endlink where the workflow is run. RevEng is also the owner of the
+data points, structures where data points are organized and computed surfaces
+and edges. An overview is given in the figure below. 
+
+\image html datastructure.png "Data structure" width=600px
+
+The triangulation is represented by an \link Go::ftPointSet ftPointSet\endlink 
+stored in RevEng. Segmented points are orgianized in \link Go::RevEngRegion RevEngRegion\endlink. RevEngRegion contains pointers to \link Go::RevEngPoint RevEngPoint\endlink
+inherited from \link Go::ftSamplePoint ftSamplePoint\endlink. The points themselves
+are still stored in ftPointSet while the point pointers are moved between regions as
+the computation proceedes. Computed surfaces are stored in 
+\link Go::HedgeSurface HedgeSurface\endlink, which again is stored in RevEng. A
+surface is connected to one region, while a region may have a surface connected.
+HedgeSurface is inherited from \link Go::ftSurface ftSurface\endlink, which is
+input to the adjacency analysis, see \ref topology. A
+\link Go::RevEngEdge RevEngEdge\endlink is computed from two surfaces but linked
+to the associated regions. The instance itself is stored in RevEng. RevEngEdge also
+contains pointers to regions associated to the foreseen blend surface and to the 
+blend region collecting these associated regions when the blend surface is created.
+\link Go::RevEngUtils RevEngUtils\endlink provides some utility functionality to the 
+reverse engineering process.
+*/
+#endif // _REVERSEENGINEERING_DOXYMAIN_H
diff --git a/compositemodel/src/HedgeSurface.C b/compositemodel/src/HedgeSurface.C
new file mode 100644
index 00000000..58c95d12
--- /dev/null
+++ b/compositemodel/src/HedgeSurface.C
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#include "GoTools/compositemodel/HedgeSurface.h"
+#include "GoTools/compositemodel/RevEngRegion.h"
+#include "GoTools/compositemodel/RevEngUtils.h"
+#include "GoTools/geometry/ParamSurface.h"
+#include "GoTools/geometry/BoundedSurface.h"
+#include "GoTools/geometry/ElementarySurface.h"
+#include "GoTools/geometry/Plane.h"
+#include "GoTools/geometry/Cylinder.h"
+#include "GoTools/geometry/Cone.h"
+#include "GoTools/geometry/CurveOnSurface.h"
+#include "GoTools/geometry/BoundedUtils.h"
+#include "GoTools/creators/TrimCrvUtils.h"
+#include "GoTools/creators/TrimUtils.h"
+#include "GoTools/geometry/Factory.h"
+#include "GoTools/geometry/GoTools.h"
+#include "GoTools/geometry/ObjectHeader.h"
+#include <fstream>
+
+//#define DEBUG_TRIM
+
+using namespace Go;
+using std::vector;
+
+// //===========================================================================
+// HedgeSurface::HedgeSurface()
+//   : ftSurface()
+// //===========================================================================
+// {
+// }
+
+//===========================================================================
+HedgeSurface::HedgeSurface()
+  : ftSurface()
+//===========================================================================
+{
+}
+
+//===========================================================================
+HedgeSurface::HedgeSurface(shared_ptr<ParamSurface> sf, RevEngRegion *region)
+  : ftSurface(sf, -1)
+//===========================================================================
+{
+  regions_.push_back(region);
+  bbox_ = region->boundingBox();
+  surf_code_ = SURF_TYPE_UNDEF;
+}
+
+//===========================================================================
+HedgeSurface::HedgeSurface(shared_ptr<ParamSurface> sf,
+			   vector<RevEngRegion*>& region)
+  : ftSurface(sf, -1), regions_(region)
+//===========================================================================
+{
+  if (region.size() > 0)
+    {
+      bbox_ = region[0]->boundingBox();
+      for (size_t ki=1; ki<region.size(); ++ki)
+	bbox_.addUnionWith(region[ki]->boundingBox());
+    }
+  surf_code_ = SURF_TYPE_UNDEF;
+}
+
+
+//===========================================================================
+HedgeSurface::~HedgeSurface()
+//===========================================================================
+{
+}
+
+//===========================================================================
+int HedgeSurface::numPoints()
+//===========================================================================
+{
+  int num = 0;
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    num += regions_[ki]->numPoints();
+  return num;
+ }
+
+//===========================================================================
+ClassType HedgeSurface::instanceType(int& code)
+//===========================================================================
+{
+  code = surf_code_;
+  ClassType type = surface()->instanceType();
+  if (type == Class_BoundedSurface)
+    {
+      shared_ptr<ParamSurface> surf = surface();
+      shared_ptr<BoundedSurface> bdsurf =
+	dynamic_pointer_cast<BoundedSurface,ParamSurface>(surf);
+      type = bdsurf->underlyingSurface()->instanceType();
+    }
+  return type;
+}
+
+
+//===========================================================================
+bool HedgeSurface::updateSurfaceWithAxis(Point axis[3], int ix, double tol,
+					 double angtol)
+//===========================================================================
+{
+  bool updated = false;
+  int code = -1;
+  ClassType type = instanceType(code);
+  if (type == Class_Plane)
+    updated = updatePlaneWithAxis(axis, ix, tol, angtol);
+  else if (type == Class_Cylinder)
+    updated = updateCylinderWithAxis(axis, ix, tol, angtol);
+
+  return updated;
+}
+
+//===========================================================================
+bool HedgeSurface::updatePlaneWithAxis(Point axis[3], int ix, double tol,
+				       double angtol)
+//===========================================================================
+{
+  shared_ptr<Plane> init_plane = dynamic_pointer_cast<Plane,ParamSurface>(surf_);
+  if (!init_plane.get())
+    return false;
+  Point loc = init_plane->location();
+  Point mid(0.0, 0.0, 0.0);
+  double wgt = 1.0/(double)numPoints();
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    for (auto it=regions_[ki]->pointsBegin(); it!=regions_[ki]->pointsEnd(); ++it)
+      {
+	Vector3D pos0 = (*it)->getPoint();
+	Point pos(pos0[0], pos0[1], pos0[2]);
+	Point vec = pos - loc;
+	Point pos2 = loc + (vec*axis[ix])*axis[ix];
+	mid += wgt*pos2;
+      }
+
+  shared_ptr<Plane> plane(new Plane(mid, axis[ix], axis[(ix+1)%3]));
+
+  // Check accuracy
+  bool updated = checkAccuracyAndUpdate(plane, tol, angtol);
+  return updated;
+}
+
+//===========================================================================
+bool HedgeSurface::updateCylinderWithAxis(Point axis[3], int ix, double tol,
+					  double angtol)
+//===========================================================================
+{
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > points;
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    points.push_back(std::make_pair(regions_[ki]->pointsBegin(),
+				    regions_[ki]->pointsEnd()));
+  
+  Point pos;
+  double rad;
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  RevEngUtils::computeCylPosRadius(points, low, high, axis[ix], axis[(ix+1)%3],
+				   axis[(ix+2)%3], pos, rad);
+
+  // Select x-axis
+  shared_ptr<ElementarySurface> elem =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf_);
+  int ix2 = (ix+2)%3;
+  if (elem.get())
+    {
+      Point dir = elem->direction2();
+      double ang1 = dir.angle(axis[(ix+1)%3]);
+      ang1 = std::min(ang1, M_PI-ang1);
+      double ang2 = dir.angle(axis[(ix+2)%3]);
+      ang2 = std::min(ang1, M_PI-ang2);
+      if (ang1 < ang2)
+	ix2 = (ix+1)%3;
+    }
+  shared_ptr<Cylinder> cyl(new Cylinder(rad, pos, axis[ix], axis[ix2]));
+  
+  // Check accuracy
+  bool updated = checkAccuracyAndUpdate(cyl, tol, angtol);
+  return updated;
+}
+
+//===========================================================================
+bool HedgeSurface::checkAccuracyAndUpdate(shared_ptr<ParamSurface> surf,
+					  double tol, double angtol)
+//===========================================================================
+{
+  bool updated = false;
+  vector<vector<pair<double, double> > > dist_ang(regions_.size());
+  vector<double> maxd(regions_.size()), avd(regions_.size());
+  vector<int> num_in(regions_.size());
+  vector<int> num2_in(regions_.size());
+  vector<vector<double> > parvals(regions_.size());
+  int all_in = 0;
+  double avd_all = 0.0;
+  double fac = 1.0/(double)numPoints();
+  vector<int> sfflag(regions_.size(), NOT_SET);
+  bool sfOK = true;
+  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		  surf->instanceType() == Class_Cone);
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      vector<RevEngPoint*> inpt, outpt;
+      RevEngUtils::distToSurf(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd(),
+			      surf, tol, maxd[ki], avd[ki], num_in[ki], num2_in[ki],
+			      inpt, outpt, parvals[ki], dist_ang[ki], angtol);
+      all_in += num_in[ki];
+      avd_all += fac*regions_[ki]->numPoints()*avd[ki];
+      sfflag[ki] = regions_[ki]->defineSfFlag(0, tol, num_in[ki], num2_in[ki],
+					      avd[ki], cyllike);
+      if (sfflag[ki] == NOT_SET)
+	sfOK = false;
+    }
+
+  if (sfOK) //all_in > numPoints()/2 && avd_all <= tol)
+    {
+      updated = true;
+      for (size_t ki=0; ki<regions_.size(); ++ki)
+	{
+	  size_t kj=0;
+	  for (auto it=regions_[ki]->pointsBegin(); it!=regions_[ki]->pointsEnd();
+	       ++it, ++kj)
+	    {
+	      (*it)->setPar(Vector2D(parvals[ki][2*kj],parvals[ki][2*kj+1]));
+	      (*it)->setSurfaceDist(dist_ang[ki][kj].first, dist_ang[ki][kj].second);
+	    }
+	  regions_[ki]->setAccuracy(maxd[ki], avd[ki], num_in[ki], num2_in[ki]);
+	  regions_[ki]->computeDomain();
+	  regions_[ki]->setSurfaceFlag(sfflag[ki]);
+	}
+
+      replaceSurf(surf);
+    }
+  return updated;
+}
+
+//===========================================================================
+bool HedgeSurface::isCompatible(HedgeSurface* other, double angtol, double approx_tol, ClassType& type, double& score)
+//===========================================================================
+{
+  score = std::numeric_limits<double>::max();
+  int code1 = -1, code2 = -1;
+  ClassType type1 = instanceType(code1);
+  ClassType type2 = other->instanceType(code2);
+
+  shared_ptr<ParamSurface> surf1 = surface();
+  shared_ptr<ElementarySurface> psurf1 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+  if (!psurf1.get())
+    {
+      shared_ptr<BoundedSurface> bdsf1 =
+	dynamic_pointer_cast<BoundedSurface,ParamSurface>(surf1);
+      if (bdsf1.get())
+	{
+	  surf1 = bdsf1->underlyingSurface();
+	  psurf1 = dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+	}
+    }
+  
+  shared_ptr<ParamSurface> surf2 = other->surface();
+  shared_ptr<ElementarySurface> psurf2 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+  if (!psurf2.get())
+    {
+      shared_ptr<BoundedSurface> bdsf2 =
+	dynamic_pointer_cast<BoundedSurface,ParamSurface>(surf2);
+      if (bdsf2.get())
+	{
+	  surf2 = bdsf2->underlyingSurface();
+	  psurf2 = dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+	}
+    }
+
+  if (!psurf1.get())
+    {
+      RevEngRegion *preg = 0;
+      int numpt = 0;
+      int nreg = numRegions();
+      for (int ka=0; ka<nreg; ++ka)
+	{
+	  RevEngRegion *reg =  getRegion(ka);
+	  if (reg->hasBaseSf())
+	    {
+	      double maxdp, avdp;
+	      int num_inp, num2_inp;
+	      int curr_numpt = reg->numPoints();
+	      reg->getBaseDist(maxdp, avdp, num_inp, num2_inp);
+	      if (avdp < approx_tol && num_inp > curr_numpt/2 && curr_numpt > numpt)
+		{
+		  preg = reg;
+		  numpt = curr_numpt;
+		}
+	    }
+	}
+      if (preg)
+	{
+	  psurf1 =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(preg->getBase());
+	  type1 = preg->getBase()->instanceType();
+	}
+    }
+  
+  if (!psurf2.get())
+    {
+      RevEngRegion *preg = 0;
+      int numpt = 0;
+      int nreg = other->numRegions();
+      for (int ka=0; ka<nreg; ++ka)
+	{
+	  RevEngRegion *reg =  other->getRegion(ka);
+	  if (reg->hasBaseSf())
+	    {
+	      double maxdp, avdp;
+	      int num_inp, num2_inp;
+	      int curr_numpt = reg->numPoints();
+	      reg->getBaseDist(maxdp, avdp, num_inp, num2_inp);
+	      if (avdp < approx_tol && num_inp > curr_numpt/2 && curr_numpt > numpt)
+		{
+		  preg = reg;
+		  numpt = curr_numpt;
+		}
+	    }
+	}
+      if (preg)
+	{
+	  psurf2 =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(preg->getBase());
+	  type2 = preg->getBase()->instanceType();
+	}
+    }
+  
+  if (!psurf1.get() || !psurf2.get())
+    return false;
+  if (type1 != type2 || code1 != code2)
+    return false;  // Not the same surface type
+  type = type1;
+
+  Point loc1 = psurf1->location();
+  Point loc2 = psurf2->location();
+  Point vec1 = psurf1->direction();
+  Point vec2 = psurf2->direction();
+  double rad1 = psurf1->radius(0.0, 0.0);  // Parameters must be set properly for cone
+  double rad2 = psurf2->radius(0.0, 0.0);
+  double smallrad1 = psurf1->radius2(0.0, 0.0);
+  double smallrad2 = psurf2->radius2(0.0, 0.0);
+  vec1.normalize_checked();
+  vec2.normalize_checked();
+
+  int sgn = (type1 == Class_Plane) ? -1 : 1;
+  double ang = vec1.angle(vec2);
+  ang = std::min(ang, M_PI-ang);
+
+  double dlim = (rad1 < 0.0) ? approx_tol : std::max(0.05*rad1, approx_tol);
+  double anglim = 10.0*angtol;
+  double eps = 1.0e-8;
+  if (ang > anglim)
+    return false;
+  // if (fabs(rad2-rad1) > dlim && fabs(smallrad2-smallrad1) < eps)
+  //   return false;
+  if (rad1 >= 0.0 && rad2 >= 0.0 &&
+      fabs(rad2-rad1) > std::min(rad1, rad2) && fabs(smallrad2-smallrad1) < eps)
+    return false;
+  else if (smallrad1 > 0.0 &&
+	   (rad1 < rad2-smallrad2 || rad1 > rad2+smallrad2 ||
+	    rad2 < rad1-smallrad1 || rad2 > rad1+smallrad1))
+    return false;
+    
+  double pdist1 = 0.0, pdist2 = 0.0;
+  if (type1 == Class_Plane)
+    {
+      Point loc2_0 = loc2 - ((loc2-loc1)*vec1)*vec1;
+      Point loc1_0 = loc1 - ((loc2-loc1)*vec2)*vec2;
+      pdist1 = loc2.dist(loc2_0);
+      pdist2 = loc1.dist(loc1_0);
+      pdist1 = pdist2 = std::min(pdist1, pdist2);
+      if (pdist1 > 2.0*dlim || pdist2 > 2.0*dlim)    
+	return false;
+    }
+  else if (type1 == Class_Cylinder)
+    {
+      Point loc2_0 = loc1 + ((loc2-loc1)*vec1)*vec1;
+      pdist1 = loc2.dist(loc2_0);
+      if (pdist1 + fabs(rad2-rad1) > std::min(rad1, rad2))
+      // if (pdist1 > dlim)
+	return false;
+    }
+  else if (type1 == Class_Torus)
+    {
+      pdist1 = loc1.dist(loc2);
+      if (pdist1 > 2.0*dlim || pdist2 > 2.0*dlim)    
+	return false;
+    }
+
+  score = 10.0*ang + fabs(rad2-rad1) + fabs(smallrad2-smallrad1) +
+    pdist1 + pdist2;
+  return true;
+}
+
+//===========================================================================
+bool HedgeSurface::hasBaseSf()
+//===========================================================================
+{
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    if (regions_[ki]->hasBaseSf())
+      return true;
+  return false;
+}
+
+//===========================================================================
+void HedgeSurface::ensureSurfaceBounded()
+//===========================================================================
+{
+  shared_ptr<ParamSurface> surf = surface();
+  shared_ptr<ElementarySurface> elemsf =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+  if (!elemsf.get())
+    return;  // Always bounded
+  if (!elemsf->isBounded())
+    {
+      double diag = bbox_.low().dist(bbox_.high());
+      if (elemsf->instanceType() == Class_Plane)
+	elemsf->setParameterBounds(-0.6*diag, -0.6*diag, 0.6*diag, 0.6*diag);
+      else if (elemsf->instanceType() == Class_Cylinder)
+	{
+	  shared_ptr<Cylinder> cyl =
+	    dynamic_pointer_cast<Cylinder,ElementarySurface>(elemsf);
+	  cyl->setParamBoundsV(-0.6*diag, 0.6*diag);
+	}
+      else if (elemsf->instanceType() == Class_Cone)
+	{
+	  shared_ptr<Cone> cone =
+	    dynamic_pointer_cast<Cone,ElementarySurface>(elemsf);
+	  cone->setParamBoundsV(-0.6*diag, 0.6*diag);
+	}
+    }
+}
+
+//===========================================================================
+bool HedgeSurface::isTangential(HedgeSurface* surf)
+//===========================================================================
+{
+  return false;  // To be implemented properly
+}
+ 
+//===========================================================================
+void HedgeSurface::doTrim(vector<shared_ptr<CurveOnSurface> >& int_cvs,
+			  shared_ptr<BoundedSurface>& bdsf,
+			  double tol,
+			  vector<shared_ptr<HedgeSurface> >& added_sfs)
+//===========================================================================
+{
+  vector<shared_ptr<BoundedSurface> > trim_sfs;
+  if (int_cvs.size() > 0)
+    {
+      trim_sfs = BoundedUtils::splitWithTrimSegments(bdsf, int_cvs, tol);
+    }
+
+  std::ofstream of1("curr_trim.g2");
+  for (size_t ki=0; ki<trim_sfs.size(); ++ki)
+    {
+      trim_sfs[ki]->writeStandardHeader(of1);
+      trim_sfs[ki]->write(of1);
+    }
+
+  if (trim_sfs.size() <= 1)
+    {
+      // Do something to complement the intersection results
+      int stop_break1 = 1;
+    }
+
+  // Assume, for the time being, that no region is split between sub surfaces
+  vector<shared_ptr<BoundedSurface> > valid_trim;
+  vector<vector<RevEngRegion*> > regs;
+  for (size_t ki=0; ki<trim_sfs.size(); ++ki)
+    {
+      vector<RevEngRegion*> curr_regs;
+      for (size_t kj=0; kj<regions_.size(); ++kj)
+	{
+	  int numpt = regions_[kj]->numPoints();
+	  int inside = 2;
+	  for (int ka=0; ka<numpt; ++ka)
+	    {
+	      RevEngPoint *pt = regions_[kj]->getPoint(ka);
+	      Vector2D parpt = pt->getPar();
+	      inside = trim_sfs[ki]->inDomain2(parpt[0], parpt[1]);
+	      if (inside != 2)
+		break;  // Not on a boundary
+	    }
+	  if (inside == 1)
+	    curr_regs.push_back(regions_[kj]);
+	}
+      if (curr_regs.size() > 0)
+	{
+	  valid_trim.push_back(trim_sfs[ki]);
+	  regs.push_back(curr_regs);
+	}
+    }
+
+  for (size_t ki=1; ki<valid_trim.size(); ++ki)
+    {
+      shared_ptr<HedgeSurface> hedge(new HedgeSurface(valid_trim[ki], regs[ki]));
+      for (size_t kj=0; kj<regs[ki].size(); ++kj)
+	{
+	  (void)removeRegion(regs[ki][kj]);
+	  regs[ki][kj]->setHedge(hedge.get());
+	}
+      added_sfs.push_back(hedge);
+    }
+  if (valid_trim.size() > 0)
+    replaceSurf(valid_trim[0]);
+  
+  
+  int stop_break = 1;
+}
+
+//===========================================================================
+bool HedgeSurface::removeRegion(RevEngRegion* reg)
+//===========================================================================
+{
+  auto it = std::find(regions_.begin(), regions_.end(), reg);
+  if (it != regions_.end())
+    {
+      regions_.erase(it);
+      if (regions_.size() > 0)
+	{
+	  bbox_ = regions_[0]->boundingBox();
+	  for (size_t ki=1; ki<regions_.size(); ++ki)
+	    bbox_.addUnionWith(regions_[ki]->boundingBox());
+	}
+    }
+  return false;
+}
+
+//===========================================================================
+void HedgeSurface::addRegion(RevEngRegion* reg)
+//===========================================================================
+{
+    regions_.push_back(reg);
+    if (bbox_.dimension() == 0)
+      bbox_ = reg->boundingBox();
+    else
+      bbox_.addUnionWith(reg->boundingBox());
+}
+
+//===========================================================================
+void HedgeSurface::limitSurf(double diag)
+//===========================================================================
+{
+  shared_ptr<ParamSurface> surf = surface();
+  shared_ptr<ElementarySurface> elemsf =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+  if (elemsf.get() && !elemsf->isBounded())
+    {
+      if (diag < 0.0)
+	diag = bbox_.low().dist(bbox_.high());
+      double domain[4];
+      regions_[0]->getDomain(domain);
+      double midu = 0.5*(domain[0]+domain[1]);
+      double midv = 0.5*(domain[2]+domain[3]);
+      double fac = 1.0;
+      shared_ptr<Plane> plane = dynamic_pointer_cast<Plane,ParamSurface>(surf);
+      shared_ptr<Cylinder> cyl = dynamic_pointer_cast<Cylinder,ParamSurface>(surf);
+      shared_ptr<Cone> cone = dynamic_pointer_cast<Cone,ParamSurface>(surf);
+      if (plane.get())
+	plane->setParameterBounds(midu-fac*diag, midv-fac*diag,
+				  midu+fac*diag, midv+fac*diag);
+      else if (cyl.get())
+	cyl->setParamBoundsV(midv-fac*diag, midv+fac*diag);
+      else if (cone.get())
+	cone->setParamBoundsV(midv-fac*diag, midv+fac*diag);
+    }
+}
+
+//===========================================================================
+bool HedgeSurface::trimWithPoints(double aeps)
+//===========================================================================
+{
+  // Extract data points
+  int del = 5;
+  int num_pts = numPoints();
+  if (num_pts < 10)
+    return false;
+  vector<double> data(del*num_pts);
+  int num_reg = numRegions();
+  vector<double> extent(2*del);   // Limits for points in all coordinates
+  for (int ki=0, kj=0; ki<num_reg; ++ki)
+    {
+      RevEngRegion *reg = getRegion(ki);
+      int npts = reg->numPoints();
+      for (int kr=0; kr<npts; ++kr, ++kj)
+	{
+	  RevEngPoint *pt = reg->getPoint(kr);
+	  Vector2D uv = pt->getPar();
+	  Vector3D xyz = pt->getPoint();
+	  data[del*kj] = uv[0];
+	  data[del*kj+1] = uv[1];
+	  data[del*kj+2] = xyz[0];
+	  data[del*kj+3] = xyz[1];
+	  data[del*kj+4] = xyz[2];
+	}
+    }
+  for (int kj=0; kj<del; ++kj)
+    extent[2*kj] = extent[2*kj+1] = data[kj];
+  for (int ki=1; ki<num_pts; ++ki)
+    for (int kj=0; kj<del; ++kj)
+      {
+	extent[2*kj] = std::min(extent[2*kj],data[ki*del+kj]);
+	extent[2*kj+1] = std::max(extent[2*kj+1],data[ki*del+kj]);
+      }
+  
+
+  // Rough trimming curve
+  double tol = 1.0e-5;
+  int max_rec = 1;
+  int nmb_div = std::min(num_pts/70, 8); //15;
+
+  if (extent[1]-extent[0] < tol || extent[3]-extent[2] < tol)
+    return false;
+    // Compute trimming seqence
+  bool only_outer = true;
+  vector<vector<double> > seqs;
+  //TrimUtils trimutil(points2, 1, domain);
+  TrimUtils trimutil(&data[0], num_pts, del-2, &extent[0]);
+  trimutil.computeTrimSeqs(max_rec, nmb_div, seqs, only_outer);
+  
+  // Compute trimming loop
+  // First extract parts of the trimming sequences following iso trim curves
+  double udel, vdel;
+  trimutil.getDomainLengths(udel, vdel);
+
+  int nmb_loops = only_outer ? std::min(1,(int)seqs.size()) : (int)seqs.size();
+  vector<vector<vector<double> > >  all_seqs(nmb_loops);
+  if (nmb_loops == 0)
+    return false;
+  for (int kh=0; kh<nmb_loops; ++kh)
+    all_seqs[kh].push_back(seqs[kh]);
+
+  int nmb_match = 4;
+  double eps = std::max(udel, vdel);
+  vector<vector<shared_ptr<CurveOnSurface> > > loop(nmb_loops);
+  for (int kh=0; kh<nmb_loops; ++kh)
+    {
+      for (int kj=0; kj<4; ++kj)
+      	{
+      	  int ix = (kj < 2) ? 0 : 1;
+      	  for (int ki=0; ki<(int)all_seqs[kh].size();)
+      	    {
+      	      vector<vector<double> > split_seqs1 = 
+      		TrimCrvUtils::extractConstParSeqs(all_seqs[kh][ki], ix, 
+      						  extent[kj], nmb_match, 
+      						  tol, eps);
+      	      if (split_seqs1.size() > 1 || split_seqs1[0].size() == 4)
+      		{
+      		  all_seqs[kh].erase(all_seqs[kh].begin()+ki);
+      		  all_seqs[kh].insert(all_seqs[kh].begin()+ki, 
+      				      split_seqs1.begin(), split_seqs1.end());
+      		  ki += (int)split_seqs1.size();
+      		}
+      	      else
+      		++ki;
+      	    }
+      	}
+
+      // Split the remaining sequences from the outer loop in kinks
+      double kink_tol = 5e-01; // 0.1 deg => 5.7 degrees.
+      vector<vector<double> > split_seqs;
+      for (size_t ki=0; ki<all_seqs[kh].size(); ++ki)
+	{
+	  vector<vector<double> > curr_seqs = 
+	    TrimCrvUtils::splitCurvePointsInKinks(all_seqs[kh][ki], kink_tol);
+	  split_seqs.insert(split_seqs.end(), curr_seqs.begin(), curr_seqs.end());
+	}
+
+      // Ensure a closed trimming loop
+      TrimCrvUtils::makeConnectedLoop(split_seqs, tol);
+  
+      // Create trimming curves
+      const int par_dim = 2;
+      const int max_iter = 5;
+      vector<shared_ptr<SplineCurve> > par_cvs;
+      for (size_t ki = 0; ki < split_seqs.size(); ++ki)
+	{
+	  shared_ptr<SplineCurve> spline_cv_appr_2d
+	    (TrimCrvUtils::approximateTrimPts(split_seqs[ki], par_dim, eps, 
+					      max_iter));
+	  par_cvs.push_back(spline_cv_appr_2d);
+	}
+  
+      // The curve should be CCW.
+      // Assume one outer and the rest inner (this is not necessarily true)
+      const double int_tol = 1e-06;
+      vector<shared_ptr<ParamCurve> > par_cvs2(par_cvs.begin(), par_cvs.end());
+      bool loop_is_ccw = LoopUtils::loopIsCCW(par_cvs2, eps, int_tol);
+      if ((kh==0 && !loop_is_ccw) || (kh>0 && loop_is_ccw))
+	{
+	  //MESSAGE("We should change direction of the loop cv!");
+	  for (size_t ki = 0; ki < par_cvs.size(); ++ki)
+	    {
+	      par_cvs[ki]->reverseParameterDirection();
+	    }
+	  reverse(par_cvs.begin(), par_cvs.end());
+	}
+      loop[kh].resize(par_cvs.size());
+      for (size_t ki = 0; ki < par_cvs.size(); ++ki)
+	{
+	  loop[kh][ki] =
+	    shared_ptr<CurveOnSurface>(new CurveOnSurface(surface(), par_cvs[ki], true));
+	}
+    }
+#ifdef DEBUG_TRIM
+  std::ofstream fileout("trimcrvs.g2");
+  for (size_t kh=0; kh<loop.size(); ++kh)
+    {
+      for (size_t kr=0; kr<loop[kh].size(); ++kr)
+	{
+	  loop[kh][kr]->parameterCurve()->writeStandardHeader(fileout);
+	  loop[kh][kr]->parameterCurve()->write(fileout);
+	}
+    }
+  if (del != 3)
+    {
+      fileout << "400 1 0 0" << std::endl;
+      fileout << num_pts << std::endl;
+      for (int ka=0; ka<num_pts; ++ka)
+	{
+	  fileout << data[ka*del] << " " << data[ka*del+1] << " 0.0" << std::endl;
+	}
+    }
+#endif
+  shared_ptr<BoundedSurface> bdsf(new BoundedSurface(surface(), loop, aeps));
+  replaceSurf(bdsf);
+
+  return true;
+}
+
+//===========================================================================
+void HedgeSurface::store(std::ostream& os) const
+//===========================================================================
+{
+  os << id_ << " " << surf_code_ << std::endl;
+  surf_->writeStandardHeader(os);
+  surf_->write(os);
+  int profile = (profile_.get()) ? 1 : 0;
+  os << profile << std::endl;
+  if (profile_.get())
+    {
+      profile_->writeStandardHeader(os);
+      profile_->write(os);
+      os << sweep1_ << " " << sweep2_ << std::endl;
+    }
+}
+
+//===========================================================================
+void HedgeSurface::read(std::istream& is)
+//===========================================================================
+{
+  GoTools::init();
+  is >> id_ >> surf_code_;
+  ObjectHeader header;
+  header.read(is);
+  shared_ptr<GeomObject> obj(Factory::createObject(header.classType()));
+  obj->read(is);
+  surf_ =  dynamic_pointer_cast<ParamSurface,GeomObject>(obj);
+  int profile;
+  is >> profile;
+  if (profile)
+    {
+      ObjectHeader header2;
+      header2.read(is);
+      shared_ptr<GeomObject> obj2(Factory::createObject(header2.classType()));
+      obj2->read(is);
+      profile_ =  dynamic_pointer_cast<SplineCurve,GeomObject>(obj);
+      sweep1_ = Point(3);
+      sweep1_.read(is);
+      sweep2_ = Point(3);
+      sweep2_.read(is);
+    }
+}
diff --git a/compositemodel/src/ImplicitApprox.C b/compositemodel/src/ImplicitApprox.C
new file mode 100644
index 00000000..9f507cf2
--- /dev/null
+++ b/compositemodel/src/ImplicitApprox.C
@@ -0,0 +1,940 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#include "GoTools/compositemodel/ImplicitApprox.h"
+#include "GoTools/compositemodel/RevEngPoint.h"
+#include "GoTools/implicitization/ImplicitizePointCloudAlgo.h"
+#include "GoTools/geometry/PointCloud.h"
+#include "GoTools/geometry/ObjectHeader.h"
+#include "GoTools/geometry/PointCloud.h"
+#include "GoTools/geometry/LineCloud.h"
+#include "GoTools/geometry/SweepSurfaceCreator.h"
+#include "GoTools/geometry/SplineCurve.h"
+#include "GoTools/geometry/SplineSurface.h"
+#include "GoTools/utils/BoundingBox.h"
+#include "GoTools/compositemodel/SurfaceModel.h"
+#include "GoTools/compositemodel/CompositeModelFactory.h"
+#include "GoTools/compositemodel/ftPlane.h"
+#include "GoTools/compositemodel/ftCurve.h"
+#include "GoTools/implicitization/BernsteinPoly.h"
+#include "newmat.h"
+#include "newmatap.h"
+#include "sisl.h"
+#include <fstream>
+
+using namespace Go;
+using std::vector;
+
+//===========================================================================
+ImplicitApprox::ImplicitApprox()
+  : eps_(1.0e-12)
+//===========================================================================
+{
+}
+
+//===========================================================================
+ImplicitApprox::~ImplicitApprox()
+//===========================================================================
+{
+}
+
+//===========================================================================
+void ImplicitApprox::approx(vector<RevEngPoint*> points, int degree)
+//===========================================================================
+{
+  // Extract xyz values
+  vector<Vector3D> xyz(points.size());
+  for (size_t ki=0; ki<points.size(); ++ki)
+    xyz[ki] = points[ki]->getPoint();
+
+  PointCloud3D pointset(xyz);
+
+  // Implicitize
+  degree_ = degree;
+  ImplicitizePointCloudAlgo implicitize(pointset, degree);
+  implicitize.perform();
+  
+  // Get result
+  implicitize.getResultData(implicit_, bc_, sigma_min_);
+  
+  // Differentiate
+  Vector4D bdir1(1.0, 0.0, 0.0, 0.0);
+  Vector4D bdir2(0.0, 1.0, 0.0, 0.0);
+  Vector4D bdir3(0.0, 0.0, 1.0, 0.0);
+  Vector4D bdir4(0.0, 0.0, 0.0, 1.0);
+  implicit_.deriv(1, bdir1, deriv1_);
+  implicit_.deriv(1, bdir2, deriv2_);
+  implicit_.deriv(1, bdir3, deriv3_);
+  implicit_.deriv(1, bdir4, deriv4_);
+
+}
+
+//===========================================================================
+void ImplicitApprox::approxPoints(vector<Point> points, int degree)
+//===========================================================================
+{
+  // Extract xyz values
+  vector<Vector3D> xyz(points.size());
+  for (size_t ki=0; ki<points.size(); ++ki)
+    xyz[ki] = Vector3D(points[ki].begin());
+
+  PointCloud3D pointset(xyz);
+
+  // Implicitize
+  degree_ = degree;
+  ImplicitizePointCloudAlgo implicitize(pointset, degree);
+  implicitize.perform();
+  
+  // Get result
+  implicitize.getResultData(implicit_, bc_, sigma_min_);
+  
+  // Differentiate
+  Vector4D bdir1(1.0, 0.0, 0.0, 0.0);
+  Vector4D bdir2(0.0, 1.0, 0.0, 0.0);
+  Vector4D bdir3(0.0, 0.0, 1.0, 0.0);
+  Vector4D bdir4(0.0, 0.0, 0.0, 1.0);
+  implicit_.deriv(1, bdir1, deriv1_);
+  implicit_.deriv(1, bdir2, deriv2_);
+  implicit_.deriv(1, bdir3, deriv3_);
+  implicit_.deriv(1, bdir4, deriv4_);
+
+}
+
+//===========================================================================
+void ImplicitApprox::approx(vector<pair<vector<RevEngPoint*>::iterator,
+			    vector<RevEngPoint*>::iterator> >& points,
+			    int degree)
+//===========================================================================
+{
+  // Extract xyz values
+  vector<Vector3D> xyz;
+  for (size_t kj=0; kj<points.size(); ++kj)
+    {
+      for (auto it=points[kj].first; it!=points[kj].second; ++it)
+	{
+	  Vector3D curr = (*it)->getPoint();
+	  xyz.push_back(curr);
+	}
+    }
+  PointCloud3D pointset(xyz);
+
+  // Implicitize
+  degree_ = degree;
+  ImplicitizePointCloudAlgo implicitize(pointset, degree);
+  implicitize.perform();
+  
+  // Get result
+  implicitize.getResultData(implicit_, bc_, sigma_min_);
+  
+  // Differentiate
+  Vector4D bdir1(1.0, 0.0, 0.0, 0.0);
+  Vector4D bdir2(0.0, 1.0, 0.0, 0.0);
+  Vector4D bdir3(0.0, 0.0, 1.0, 0.0);
+  Vector4D bdir4(0.0, 0.0, 0.0, 1.0);
+  implicit_.deriv(1, bdir1, deriv1_);
+  implicit_.deriv(1, bdir2, deriv2_);
+  implicit_.deriv(1, bdir3, deriv3_);
+  implicit_.deriv(1, bdir4, deriv4_);
+
+}
+
+//===========================================================================
+void ImplicitApprox::evaluate(Point& pt, double& val, Point& grad)
+//===========================================================================
+{
+  Vector3D xyz(pt[0], pt[1], pt[2]);
+  Vector4D bary = bc_.cartToBary(xyz);
+  val = implicit_(bary);
+  double d1 = deriv1_(bary);
+  double d2 = deriv2_(bary);
+  double d3 = deriv3_(bary);
+  double d4 = deriv4_(bary);
+  Vector4D dv(d1,d2,d3,d4);
+  Vector4D bary2 = bary+dv;
+  Vector3D pt2 = bc_.baryToCart(bary2);
+  Vector3D grad2 = pt2 - xyz;
+  grad = Point(grad2[0], grad2[1], grad2[2]);
+}
+
+//===========================================================================
+double ImplicitApprox::estimateDist(RevEngPoint* pt)
+//===========================================================================
+{
+  Vector3D xyz = pt->getPoint();
+  Vector4D bary = bc_.cartToBary(xyz);
+  double dist0 = implicit_(bary);
+  double d1 = deriv1_(bary);
+  double d2 = deriv2_(bary);
+  double d3 = deriv3_(bary);
+  double d4 = deriv4_(bary);
+  Vector4D dv(d1,d2,d3,d4);
+  Vector4D bary2 = bary+dv;
+  Vector3D pt2 = bc_.baryToCart(bary2);
+  Vector3D grad = pt2 - xyz;
+  double len = grad.length();
+  double dist = (len > eps_) ? dist0/len : dist0;
+
+  Point norm = pt->getLocFuncNormal();
+  Vector3D norm2(norm[0], norm[1], norm[2]);
+  norm2 *= 100;
+  Vector3D xyz2 = xyz + norm2;
+  Vector3D xyz3 = xyz - norm2;
+  Vector4D bary3 = bc_.cartToBary(xyz2);
+  Vector4D bary4 = bc_.cartToBary(xyz3);
+  BernsteinPoly line = implicit_.pickLine(bary3, bary4);
+
+  // Compute zeroes of bernstein polynomial
+  // First make sisl curve
+  int ik = degree_ + 1;
+  vector<double> et(2*ik, 0.0);  // Knot vector of line curve
+  for (int ki=0; ki<ik; ++ki)
+    et[ik+ki] = 1.0;
+  vector<double> ecoef(line.coefsBegin(), line.coefsEnd());
+  SISLCurve *qc = newCurve(ik, ik, &et[0], &ecoef[0], 1, 1, 1);
+  double zero = 0.0;
+  
+  // Intersect
+  double eps = 1.0e-6;
+  int kstat = 0;
+  int kcrv=0, kpt=0;
+  double *epar = 0;
+  SISLIntcurve **intcv = 0;
+  if (qc)
+    s1871(qc, &zero, 1, eps, &kpt, &epar, &kcrv, &intcv, &kstat);
+  if (qc)
+    freeCurve(qc);
+  
+  // Compute cartesian points and curves associated with intersections
+  double dd = std::numeric_limits<double>::max();
+  for (int kr=0; kr<kpt; ++kr)
+    {
+      Vector4D barypt = (1.0 - epar[kr])*bary3 + epar[kr]*bary4;
+      Vector3D pos = bc_.baryToCart(barypt);
+      double dd2 = xyz.dist(pos);
+      if (dd2 < dd)
+	dd = dd2;
+    }
+  if (epar) free(epar);
+  if (intcv) freeIntcrvlist(intcv, kcrv);
+  return dd; //dist;
+}
+
+//===========================================================================
+bool ImplicitApprox::projectPoint(Point point, Point dir,
+				  Point& projpos, Point& normal)
+//===========================================================================
+{
+  double len = 100.0;
+  dir.normalize();
+  
+  Point xdir(1.0, 0.0, 0.0);
+  Point ydir(0.0, 1.0, 0.0);
+  Point zdir(0.0, 0.0, 1.0);
+  double a1 = xdir.angle(dir);
+  double a2 = ydir.angle(dir);
+  double a3 = zdir.angle(dir);
+  Point dir2;
+  if (a1 > std::min(a2, a3))
+    dir2 = xdir;
+  else if (a2 > a3)
+    dir2 = ydir;
+  else
+    dir2 = zdir;
+  Point dir3 = dir%dir2;
+  dir2 = dir%dir3;
+  dir2.normalize();
+  dir3.normalize();
+  Point points[3];
+  points[0] = point;
+  points[1] = point + dir2;
+  points[2] = point + dir3;
+
+  Vector3D proj[3];
+  int ka;
+  for (ka=0; ka<3; ++ka)
+    {
+      Vector3D xyz(points[ka].begin());
+      Point p1 = points[ka] - len*dir;
+      Point p2 = points[ka] + len*dir;
+
+      Vector3D cart1(p1.begin());
+      Vector3D cart2(p2.begin());
+      Vector4D bary1 = bc_.cartToBary(Vector3D(cart1[0], cart1[1], cart1[2]));
+      Vector4D bary2 = bc_.cartToBary(Vector3D(cart2[0], cart2[1], cart2[2]));
+
+      // Pick line
+      BernsteinPoly line = implicit_.pickLine(bary1, bary2);
+
+      // Compute zeroes of bernstein polynomial
+      // First make sisl curve
+      int ik = degree_ + 1;
+      vector<double> et(2*ik, 0.0);  // Knot vector of line curve
+      for (int ki=0; ki<ik; ++ki)
+	et[ik+ki] = 1.0;
+      vector<double> ecoef(line.coefsBegin(), line.coefsEnd());
+      SISLCurve *qc = newCurve(ik, ik, &et[0], &ecoef[0], 1, 1, 1);
+      double zero = 0.0;
+
+      // Intersect
+      double eps = 1.0e-6;
+      int kstat = 0;
+      int kcrv=0, kpt=0;
+      double *epar = 0;
+      SISLIntcurve **intcv = 0;
+      if (qc)
+	s1871(qc, &zero, 1, eps, &kpt, &epar, &kcrv, &intcv, &kstat);
+      if (qc)
+	freeCurve(qc);
+      if (kpt == 0)
+	return false;
+
+      // Compute cartesian points and curves associated with intersections
+      double mindist = std::numeric_limits<double>::max();
+      for (int kr=0; kr<kpt; ++kr)
+	{
+	  Vector4D barypt = (1.0 - epar[kr])*bary1 + epar[kr]*bary2;
+		
+	  Vector3D pos = bc_.baryToCart(barypt);
+	  double dist = pos.dist(xyz);
+	  if (dist < mindist)
+	    {
+	      mindist = dist;
+	      proj[ka] = pos;
+	    }
+	}
+      if (epar) free(epar);
+      if (intcv) freeIntcrvlist(intcv, kcrv);
+      
+    }
+
+  projpos = Point(proj[0][0], proj[0][1], proj[0][2]);
+  Point pt2(proj[1][0], proj[1][1], proj[1][2]);
+  Point pt3(proj[2][0], proj[2][1], proj[2][2]);
+  
+  Point vec1 = pt2 - projpos;
+  Point vec2 = pt3 - projpos;
+  normal = vec1.cross(vec2);
+  normal.normalize_checked();
+  return true;
+}
+
+//===========================================================================
+void ImplicitApprox::visualize(vector<RevEngPoint*> points, std::ostream& os)
+//===========================================================================
+{
+  // View direction
+  Point dir = points[0]->getLocFuncNormal();
+  dir.normalize();
+
+  BoundingBox bb(3);
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      bb.addUnionWith(Point(xyz[0], xyz[1], xyz[2]));
+    }
+  Point low = bb.low();
+  Point high = bb.high();
+  Point bmid = 0.5*(low + high);
+  Point diag = high - low;
+  double diaglen = diag.length();
+
+  double gap = 1.0e-6;
+  Point xdir(1.0, 0.0, 0.0);
+  Point ydir(0.0, 1.0, 0.0);
+  Point zdir(0.0, 0.0, 1.0);
+  CompositeModelFactory factory(gap, gap, 10.0*gap, 0.01, 0.05);
+  shared_ptr<SurfaceModel> boxmod(factory.createFromBox(low-2.0*diag, xdir, ydir, 
+  							5*diag[0], 5*diag[1], 5*diag[2]));
+    
+    // Find the coordinate direction with the largest angle with the view direction
+    double a1 = xdir.angle(dir);
+    double a2 = ydir.angle(dir);
+    double a3 = zdir.angle(dir);
+    Point dir2;
+    if (a1 > std::min(a2, a3))
+      dir2 = xdir;
+    else if (a2 > a3)
+      dir2 = ydir;
+    else
+      dir2 = zdir;
+    Point dir3 = dir%dir2;
+    dir2 = dir%dir3;
+    if (dir2*(high-low) < 0.0)
+      dir2 *= -1.0;
+    if (dir3*(high-low) < 0.0)
+      dir3 *= -1.0;
+    dir2.normalize();
+    dir3.normalize();
+    double len = low.dist(high);
+    int nmb_sample = 100;
+    shared_ptr<SplineCurve> cv1(new SplineCurve(bmid-len*dir2, 0.0, bmid+len*dir2, 1.0));
+    shared_ptr<SplineCurve> cv2(new SplineCurve(bmid-len*dir3, 0.0, bmid+len*dir3, 1.0));
+    SweepSurfaceCreator sweep;
+    shared_ptr<SplineSurface> ssf(sweep.linearSweptSurface(*cv1, *cv2, bmid));
+    double del = 1.0/(double)(nmb_sample-1);
+    double p1, p2;
+    int ki, kj, kr;
+    int ik = degree_ + 1;
+    vector<double> et(2*ik, 0.0);  // Knot vector of line curve
+    for (int ki=0; ki<ik; ++ki)
+      et[ik+ki] = 1.0;
+
+    vector<double> sfpoints;
+    vector<double> vecs;
+    vector<double> linesegs;
+    vector<double> der;
+    vector<double> der2;
+    vector<double> lineder;
+    // Evaluate line
+    vector<double> tmpline;
+    for (kj=0, p2=0.0; kj<nmb_sample; ++kj, p2+=del)
+      {
+  	for (ki=0, p1=0.0; ki<nmb_sample; ++ki, p1+=del)
+  	  {
+  	    // Compute barysentric coordinates of end points of line
+  	    // First cartesian
+  	    Point sfpos = ssf->ParamSurface::point(p1,p2);
+  	    Point cart1 = sfpos + len*dir;
+  	    Point cart2 = sfpos - len*dir;
+  	    tmpline.insert(tmpline.end(), cart1.begin(), cart1.end());
+  	    tmpline.insert(tmpline.end(), cart2.begin(), cart2.end());
+
+  	    Vector4D bary1 = bc_.cartToBary(Vector3D(cart1[0], cart1[1], cart1[2]));
+  	    Vector4D bary2 = bc_.cartToBary(Vector3D(cart2[0], cart2[1], cart2[2]));
+
+  	    Vector3D tp1 = bc_.baryToCart(bary1);
+  	    Vector3D tp2 = bc_.baryToCart(bary2);
+	    
+  	    // Pick line
+  	    BernsteinPoly line = implicit_.pickLine(bary1, bary2);
+
+  	    // Compute zeroes of bernstein polynomial
+  	    // First make sisl curve
+  	    vector<double> ecoef(line.coefsBegin(), line.coefsEnd());
+  	    SISLCurve *qc = newCurve(ik, ik, &et[0], &ecoef[0], 1, 1, 1);
+  	    double zero = 0.0;
+
+  	    // Intersect
+  	    double eps = 1.0e-6;
+  	    int kstat = 0;
+  	    int kcrv=0, kpt=0;
+  	    double *epar = 0;
+  	    SISLIntcurve **intcv = 0;
+  	    if (qc)
+  	      s1871(qc, &zero, 1, eps, &kpt, &epar, &kcrv, &intcv, &kstat);
+  	    if (qc)
+  	      freeCurve(qc);
+
+  	    // Compute cartesian points and curves associated with intersections
+  	    for (kr=0; kr<kpt; ++kr)
+  	      {
+  		Vector4D barypt = (1.0 - epar[kr])*bary1 + epar[kr]*bary2;
+  		int kb;
+  		for (kb=0; kb<4; ++kb)
+  		  if (barypt[kb] < -0.001 || barypt[kb] > 1.001)
+  		    break;
+  		if (kb < 4)
+  		  continue;
+		
+  		Vector3D pos = bc_.baryToCart(barypt);
+  		sfpoints.insert(sfpoints.end(), pos.begin(), pos.end());
+
+  	      }
+	    if (epar) free(epar);
+	    if (intcv) freeIntcrvlist(intcv, kcrv);
+  	  }
+      }
+    
+    // Output
+    if (sfpoints.size() > 0)
+      {
+  	PointCloud3D ptcloud(&sfpoints[0], sfpoints.size()/3);
+  	os << "400 1 0 4 255 0 0 255" << std::endl;
+  	ptcloud.write(os);
+      }
+}
+
+//===========================================================================
+void ImplicitApprox::visualize(vector<Point> points, Point& dir, std::ostream& os)
+//===========================================================================
+{
+  BoundingBox bb(3);
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      bb.addUnionWith(points[ki]);
+    }
+  Point low = bb.low();
+  Point high = bb.high();
+  Point bmid = 0.5*(low + high);
+  Point diag = high - low;
+  double diaglen = diag.length();
+
+  double gap = 1.0e-6;
+  Point xdir(1.0, 0.0, 0.0);
+  Point ydir(0.0, 1.0, 0.0);
+  Point zdir(0.0, 0.0, 1.0);
+  CompositeModelFactory factory(gap, gap, 10.0*gap, 0.01, 0.05);
+  shared_ptr<SurfaceModel> boxmod(factory.createFromBox(low-2.0*diag, xdir, ydir, 
+							5*diag[0], 5*diag[1], 5*diag[2]));
+    
+    // Find the coordinate direction with the largest angle with the view direction
+    double a1 = xdir.angle(dir);
+    double a2 = ydir.angle(dir);
+    double a3 = zdir.angle(dir);
+    Point dir2;
+    if (a1 > std::min(a2, a3))
+      dir2 = xdir;
+    else if (a2 > a3)
+      dir2 = ydir;
+    else
+      dir2 = zdir;
+    Point dir3 = dir%dir2;
+    dir2 = dir%dir3;
+    if (dir2*(high-low) < 0.0)
+      dir2 *= -1.0;
+    if (dir3*(high-low) < 0.0)
+      dir3 *= -1.0;
+    dir2.normalize();
+    dir3.normalize();
+    double len = low.dist(high);
+    int nmb_sample = 100;
+    shared_ptr<SplineCurve> cv1(new SplineCurve(bmid-len*dir2, 0.0, bmid+len*dir2, 1.0));
+    shared_ptr<SplineCurve> cv2(new SplineCurve(bmid-len*dir3, 0.0, bmid+len*dir3, 1.0));
+    SweepSurfaceCreator sweep;
+    shared_ptr<SplineSurface> ssf(sweep.linearSweptSurface(*cv1, *cv2, bmid));
+    double del = 1.0/(double)(nmb_sample-1);
+    double p1, p2;
+    int ki, kj, kr;
+    int ik = degree_ + 1;
+    vector<double> et(2*ik, 0.0);  // Knot vector of line curve
+    for (int ki=0; ki<ik; ++ki)
+      et[ik+ki] = 1.0;
+
+    vector<double> sfpoints;
+    vector<double> vecs;
+    vector<double> linesegs;
+    vector<double> der;
+    vector<double> der2;
+    vector<double> lineder;
+    // Evaluate line
+    vector<double> tmpline;
+    for (kj=0, p2=0.0; kj<nmb_sample; ++kj, p2+=del)
+      {
+	for (ki=0, p1=0.0; ki<nmb_sample; ++ki, p1+=del)
+	  {
+	    // Compute barysentric coordinates of end points of line
+	    // First cartesian
+	    Point sfpos = ssf->ParamSurface::point(p1,p2);
+	    Point cart1 = sfpos + len*dir;
+	    Point cart2 = sfpos - len*dir;
+	    tmpline.insert(tmpline.end(), cart1.begin(), cart1.end());
+	    tmpline.insert(tmpline.end(), cart2.begin(), cart2.end());
+
+	    Vector4D bary1 = bc_.cartToBary(Vector3D(cart1[0], cart1[1], cart1[2]));
+	    Vector4D bary2 = bc_.cartToBary(Vector3D(cart2[0], cart2[1], cart2[2]));
+
+	    Vector3D tp1 = bc_.baryToCart(bary1);
+	    Vector3D tp2 = bc_.baryToCart(bary2);
+	    
+	    // Pick line
+	    BernsteinPoly line = implicit_.pickLine(bary1, bary2);
+
+	    // Compute zeroes of bernstein polynomial
+	    // First make sisl curve
+	    vector<double> ecoef(line.coefsBegin(), line.coefsEnd());
+	    SISLCurve *qc = newCurve(ik, ik, &et[0], &ecoef[0], 1, 1, 1);
+	    double zero = 0.0;
+
+	    // Intersect
+	    double eps = 1.0e-6;
+	    int kstat = 0;
+	    int kcrv=0, kpt=0;
+	    double *epar = 0;
+	    SISLIntcurve **intcv = 0;
+	    if (qc)
+	      s1871(qc, &zero, 1, eps, &kpt, &epar, &kcrv, &intcv, &kstat);
+	    if (qc)
+	      freeCurve(qc);
+
+	    // Compute cartesian points and curves associated with intersections
+	    for (kr=0; kr<kpt; ++kr)
+	      {
+		Vector4D barypt = (1.0 - epar[kr])*bary1 + epar[kr]*bary2;
+		int kb;
+		for (kb=0; kb<4; ++kb)
+		  if (barypt[kb] < -0.001 || barypt[kb] > 1.001)
+		    break;
+		if (kb < 4)
+		  continue;
+		
+		Vector3D pos = bc_.baryToCart(barypt);
+		sfpoints.insert(sfpoints.end(), pos.begin(), pos.end());
+
+	      }
+	    if (epar) free(epar);
+	    if (intcv) freeIntcrvlist(intcv, kcrv);
+	  }
+      }
+    
+    // Output
+    if (sfpoints.size() > 0)
+      {
+	PointCloud3D ptcloud(&sfpoints[0], sfpoints.size()/3);
+	os << "400 1 0 4 255 0 0 255" << std::endl;
+	ptcloud.write(os);
+      }
+}
+
+
+double fc(int deg, int power[], double coef[], double x, double y, double z)
+{
+  int nn = (deg+1)*(deg+2)*(deg+3)/6;
+  double res = 0.0;
+  for (int ki=0; ki<nn; ++ki)
+    {
+      double tmp1 = 1;
+      for (int kj=0; kj<power[4*ki]; ++kj)
+	tmp1 *= x;
+      double tmp2 = 1;
+      for (int kj=0; kj<power[4*ki+1]; ++kj)
+	tmp2 *= y;
+      double tmp3 = 1;
+      for (int kj=0; kj<power[4*ki+2]; ++kj)
+	tmp3 *= z;
+      res += (coef[ki]*tmp1*tmp2*tmp3);
+    }
+  return res;
+}
+
+double f(int deg, int power[], int ki, double x, double y, double z)
+{
+  double tmp1 = 1;
+  for (int kj=0; kj<power[4*ki]; ++kj)
+    tmp1 *= x;
+  double tmp2 = 1;
+  for (int kj=0; kj<power[4*ki+1]; ++kj)
+    tmp2 *= y;
+  double tmp3 = 1;
+  for (int kj=0; kj<power[4*ki+2]; ++kj)
+    tmp3 *= z;
+  double res = tmp1*tmp2*tmp3;
+  return res;
+}
+
+double fx(int deg, int power[], int ki, double x, double y, double z)
+{
+  double res = 0.0;
+  if (power[4*ki] > 0)
+    {
+      double tmp = (double)power[4*ki];
+      for (int kj=0; kj<power[4*ki]-1; ++kj)
+	tmp *= x;
+      for (int kj=0; kj<power[4*ki+1]; ++kj)
+	tmp *= y;
+      for (int kj=0; kj<power[4*ki+2]; ++kj)
+	tmp *= z;
+      res = tmp;
+    }
+
+  return res;
+}
+
+double fy(int deg, int power[], int ki, double x, double y, double z)
+{
+  double res = 0.0;
+  if (power[4*ki+1] > 0)
+    {
+      double tmp = (double)power[4*ki+1];
+      for (int kj=0; kj<power[4*ki+1]-1; ++kj)
+	tmp *= y;
+      for (int kj=0; kj<power[4*ki]; ++kj)
+	tmp *= x;
+      for (int kj=0; kj<power[4*ki+2]; ++kj)
+	tmp *= z;
+      res = tmp;
+    }
+  return res;
+}
+
+double fz(int deg, int power[], int ki, double x, double y, double z)
+{
+  double res = 0.0;
+  if (power[4*ki+2] > 0)
+    {
+      double tmp = (double)power[4*ki+2];
+      for (int kj=0; kj<power[4*ki+2]-1; ++kj)
+	tmp *= z;
+      for (int kj=0; kj<power[4*ki]; ++kj)
+	tmp *= x;
+      for (int kj=0; kj<power[4*ki+1]; ++kj)
+	tmp *= y;
+      res = tmp;
+    }
+  return res;
+}
+
+
+//===========================================================================
+void ImplicitApprox::polynomialSurf(vector<Point>& pos_and_der, int degree,
+				    vector<double>& coefs)
+//===========================================================================
+{
+  // Assemble matrix
+  int nmbvar = (degree+1)*(degree+2)*(degree+3)/6;
+  size_t nmb_pts = pos_and_der.size()/3;
+  vector<vector<double> > M(nmb_pts);
+  vector<vector<double> > N1(nmb_pts);
+  vector<vector<double> > N2(nmb_pts);
+  for (size_t kr=0; kr<nmb_pts; ++kr)
+    {
+      M[kr].resize(nmbvar, 0.0);
+      N1[kr].resize(nmbvar, 0.0);
+      N2[kr].resize(nmbvar, 0.0);
+    }
+
+  vector<int> power(4*nmbvar);
+  for (int ki=0, kr=0; ki<=degree; ++ki)
+    for (int kj=0; kj<=degree-ki; ++kj)
+      for (int kh=0; kh<=degree-ki-kj; ++kh, kr+=4)
+      {
+	power[kr] = ki;
+	power[kr+1] = kj;
+	power[kr+2] = kh;
+	power[kr+3] = degree-ki-kj-kh;
+      }
+  
+  for (size_t kr=0, kh=0; kr<pos_and_der.size(); kr+=3, ++kh)
+    {
+      for (int ki=0; ki<nmbvar; ++ki)
+	{
+	  double m1 = f(degree, &power[0], ki, pos_and_der[kr][0],
+			pos_and_der[kr][1], pos_and_der[kr][2]);
+	  Point tmp1(3);
+	  tmp1[0] = fx(degree, &power[0], ki, pos_and_der[kr][0],
+		       pos_and_der[kr][1], pos_and_der[kr][2]);
+	  tmp1[1] = fy(degree, &power[0], ki, pos_and_der[kr][0],
+		       pos_and_der[kr][1], pos_and_der[kr][2]);
+	  tmp1[2] = fz(degree, &power[0], ki, pos_and_der[kr][0],
+		       pos_and_der[kr][1], pos_and_der[kr][2]);
+	  double n1 = tmp1*pos_and_der[kr+1];
+	  double n2 = tmp1*pos_and_der[kr+2];
+	  M[kh][ki] += m1;
+	  N1[kh][ki] += n1;
+	  N2[kh][ki] += n2;
+	}
+    }
+
+
+  double lambda = 0.2;
+  NEWMAT::Matrix mat;
+  mat.ReSize(nmb_pts,nmbvar);
+  for (int ki=0; ki<nmb_pts; ++ki)
+    for (int kj=0; kj<nmbvar; ++kj)
+      mat.element(ki,kj) = M[ki][kj] + lambda*(N1[ki][kj] + N2[ki][kj]);
+
+  static NEWMAT::DiagonalMatrix diag;
+  static NEWMAT::Matrix V;
+  try {
+    NEWMAT::SVD(mat, diag, mat, V);
+  } catch(...) {
+    std::cout << "Exception in SVD" << std::endl;
+    return;
+  }
+
+  int write_info = 1;
+  if (write_info)
+    {
+      std::cout << "Singular values:" << std::endl;
+      for (int ki = 0; ki < nmbvar; ++ki)
+	std::cout << ki << "\t" << diag.element(ki, ki) << std::endl;
+      
+      // Write out info about singular values
+      double s_min = diag.element(nmbvar-1,nmbvar-1);
+      double s_max = diag.element(0, 0);
+      std::cout << "Implicitization:" << std::endl
+		<< "s_min = " << s_min << std::endl
+		<< "s_max = " << s_max << std::endl
+		<< "Ratio of s_min/s_max = " << s_min/s_max << std::endl;
+      std::cout << "Ratio s_min/s_next_min = " << diag.element(nmbvar-1,nmbvar-1)/diag.element(nmbvar-2,nmbvar-2) << std::endl;
+    }
+
+   coefs.resize(nmbvar);
+  for (int ki=0; ki<nmbvar; ++ki)
+    coefs[ki] = V.element(ki, nmbvar-1);
+
+}
+
+//===========================================================================
+void ImplicitApprox::polynomialSurfAccuracy(vector<Point>& pos_and_der, 
+					    int degree, vector<double>& coefs,
+					    double& maxfield, double& avfield,
+					    double& maxdist, double& avdist,
+					    int& ndiv, double& maxang,
+					    double& avang)
+//===========================================================================
+{
+  int nmbvar = (degree+1)*(degree+2)*(degree+3)/6;
+  size_t nmb_pts = pos_and_der.size()/3;
+  double eps = 1.0e-10;
+  double min_grad = 1.0e-9;
+
+  vector<int> power(4*nmbvar);
+  for (int ki=0, kr=0; ki<=degree; ++ki)
+    for (int kj=0; kj<=degree-ki; ++kj)
+      for (int kh=0; kh<=degree-ki-kj; ++kh, kr+=4)
+      {
+	power[kr] = ki;
+	power[kr+1] = kj;
+	power[kr+2] = kh;
+	power[kr+3] = degree-ki-kj-kh;
+      }
+  
+  // Test accuracy
+  maxfield = 0.0;
+  avfield = 0.0;
+  double avgradlen = 0.0;
+  double mingradlen = 1.0e8;
+  double maxgradlen = 0.0;
+  double minang = 1.0e8;
+  maxang = 0.0;
+  avang = 0.0;
+  double mindist = 1.0e8;
+  maxdist = 0.0;
+  avdist = 0.0;
+  vector<Point> out;
+  ndiv = 0;
+  for (size_t kr=0; kr<nmb_pts; kr+=3)
+    {
+      double field = 0.0;
+      double dx=0.0, dy=0.0, dz=0.0;
+      for (int ki=0; ki<nmbvar; ++ki)
+	{
+	  field += coefs[ki]*f(degree, &power[0], ki, pos_and_der[kr][0],
+			       pos_and_der[kr][1], pos_and_der[kr][2]);
+	  dx += coefs[ki]*fx(degree, &power[0], ki, pos_and_der[kr][0],
+			     pos_and_der[kr][1], pos_and_der[kr][2]);
+	  dy += coefs[ki]*fy(degree, &power[0], ki, pos_and_der[kr][0],
+			     pos_and_der[kr][1], pos_and_der[kr][2]);
+	  dz += coefs[ki]*fz(degree, &power[0], ki, pos_and_der[kr][0],
+			     pos_and_der[kr][1], pos_and_der[kr][2]);
+	}
+      Point grad(dx, dy, dz);
+      Point normc = pos_and_der[kr+1].cross(pos_and_der[kr+2]);
+      double ang = normc.angle(grad);
+      minang = std::min(minang, ang);
+      maxang = std::max(maxang, ang);
+      avang += ang;
+      double gradlen = grad.length();
+      double edist = (gradlen < 1.0e-17) ? 0.0 : fabs(field)/gradlen;
+      maxfield = std::max(maxfield, fabs(field));
+      avfield += fabs(field);
+      mingradlen = std::min(mingradlen, gradlen);
+      maxgradlen = std::max(maxgradlen, gradlen);
+      avgradlen += gradlen;
+      if (gradlen > 1.0e-10)
+	grad.normalize();
+
+      double delta = 1.0e-9;
+      Point norm = grad; //tan1[kr].cross(tan2[kr]);
+      norm.normalize();
+      double t0 = 0.0;
+      Point pos0 = pos_and_der[kr];
+      double dt = dx*norm[0] + dy*norm[1] + dz*norm[2];
+      double tdel = -field/dt;
+      double field0;
+      for (int ka=0; ka<10; ++ka)
+	{
+	  if (fabs(tdel) < delta)
+	    break;
+	  t0 += tdel;
+	  pos0 = pos_and_der[kr] + t0*norm;
+	  double dx0 = 0.0, dy0 = 0.0, dz0 = 0.0;
+	  field0 = 0.0;
+	  for (int ki=0; ki<nmbvar; ++ki)
+	    {
+	      field0 += coefs[ki]*f(degree, &power[0], ki, pos0[0],pos0[1],pos0[2]);
+	      dx0 += coefs[ki]*fx(degree, &power[0], ki, pos0[0],pos0[1],pos0[2]);
+	      dy0 += coefs[ki]*fy(degree, &power[0], ki, pos0[0],pos0[1],pos0[2]);
+	      dz0 += coefs[ki]*fz(degree, &power[0], ki, pos0[0],pos0[1],pos0[2]);
+	    }
+	  dt = dx0*norm[0] + dy0*norm[1] + dz0*norm[2];
+	  tdel = -field0/dt;
+	  int stop_break = 1;
+	}
+
+      if (fabs(field0) > fabs(field) || fabs(field0) > eps || fabs(t0) > 1.0 ||
+	  gradlen < min_grad)
+	{
+	  out.push_back(pos_and_der[kr]);
+	  ndiv++;
+	}
+      else
+	{
+	  maxdist = std::max(maxdist, fabs(t0));
+	  mindist = std::min(mindist, fabs(t0));
+	  avdist += fabs(t0);
+	}
+   }
+  avfield /= (double)nmb_pts;
+  avang /= (double)nmb_pts;
+  avgradlen /= (double)nmb_pts;
+  avdist /= (double)(nmb_pts-ndiv);
+  
+  std::cout << "Maximum field: " << maxfield << std::endl;
+  std::cout << "Average field: " << avfield << std::endl;
+  std::cout << "Minimum distance: " << mindist << std::endl;
+  std::cout << "Maximum  distance: " << maxdist << std::endl;
+  std::cout << "Average  distance: " << avdist << std::endl;
+  std::cout << "Num divergent: " << ndiv << std::endl;
+  std::cout << "Minimum angle difference: " << minang << std::endl;
+  std::cout << "Maximum angle difference: " << maxang << std::endl;
+  std::cout << "Avarage angle difference: " << avang << std::endl;
+  std::cout << "Minimum gradient length: " << mingradlen << std::endl;
+  std::cout << "Maximum gradient length: " << maxgradlen << std::endl;
+  std::cout << "Average gradient length: " << avgradlen << std::endl;
+
+  std::ofstream ofo("out.g2");
+  ofo << "400 1 0 4 50 50 155 255" << std::endl;
+  ofo << out.size() << std::endl;
+  for (int ka=0; ka<(int)out.size(); ++ka)
+    ofo << out[ka] << std::endl;
+
+  int stop_break = 1;
+  
+}
diff --git a/compositemodel/src/RevEng.C b/compositemodel/src/RevEng.C
new file mode 100644
index 00000000..f086ebaf
--- /dev/null
+++ b/compositemodel/src/RevEng.C
@@ -0,0 +1,14427 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#include "GoTools/compositemodel/RevEng.h"
+#include "GoTools/compositemodel/RevEngPoint.h"
+#include "GoTools/compositemodel/RevEngEdge.h"
+//#include "GoTools/compositemodel/RevEngRegion.h"
+#include "GoTools/compositemodel/RevEngUtils.h"
+#include "GoTools/compositemodel/HedgeSurface.h"
+#include "GoTools/compositemodel/ImplicitApprox.h"
+#include "GoTools/compositemodel/SurfaceModelUtils.h"
+#include "GoTools/utils/DirectionCone.h"
+#include "GoTools/utils/MatrixXD.h"
+#include "GoTools/creators/CurveCreators.h"
+#include "GoTools/creators/CreatorsUtils.h"
+#include "GoTools/creators/CoonsPatchGen.h"
+#include "GoTools/geometry/Cylinder.h"
+#include "GoTools/geometry/Cone.h"
+#include "GoTools/geometry/Plane.h"
+#include "GoTools/geometry/Torus.h"
+#include "GoTools/geometry/Sphere.h"
+#include "GoTools/geometry/BoundedSurface.h"
+#include "GoTools/geometry/BoundedUtils.h"
+#include "GoTools/geometry/ClosestPoint.h"
+#include "GoTools/geometry/SurfaceTools.h"
+#include "GoTools/geometry/SISLconversion.h"
+#include "GoTools/geometry/SplineDebugUtils.h"
+#include "sislP.h"
+#include <vector>
+#include <set>
+#include <fstream>
+#include <iostream> // @@ debug
+
+using namespace Go;
+using std::vector;
+using std::pair;
+using std::istream;
+using std::ostream;
+
+typedef MatrixXD<double, 3> Matrix3D;
+
+#define MAX_COLORS 12
+int colors[MAX_COLORS][3] = {
+  {255, 0, 0},
+  {0, 255, 0},
+  {0, 0, 255},
+  {255, 255, 0},
+  {255, 0, 255},
+  {0, 255, 255},
+  {128, 255, 0},
+  {255, 128, 0},
+  {128, 0, 255},
+  {255, 0, 128},
+  {0, 128, 255},
+  {0, 255, 128},
+};
+
+//#define DEBUG_DIV
+//#define DEBUG_EDGE0
+//#define DEBUG_BLEND
+//#define DEBUG_MONGE
+//#define DEBUG_ENHANCE
+//#define DEBUG_SEG
+//#define DEBUG
+//#define DEBUGONE
+//#define DEBUG_CHECK
+//#define DEBUG_PLANAR
+//#define DEBUG_AXIS
+//#define DEBUG_GROW
+//#define DEBUG_VALIDATE
+//#define DEBUG_EDGE
+//#define DEBUG_TRIANG
+//#define DEBUG_TRIM
+//#define DEBUG_MODEL
+//#define DEBUG_SMALL
+
+//===========================================================================
+RevEng::RevEng(shared_ptr<ftPointSet> tri_sf)
+  : tri_sf_(tri_sf)
+//===========================================================================
+{
+  mean_edge_len_ = 0.0;
+  int num = tri_sf_->size();
+  if (num > 0.0)
+    {
+      double fac1 = 1.0/(double)num;
+      for (int ki=0; ki<num; ++ki)
+	{
+	  double tmp_len = 0.0;
+	  ftSamplePoint* curr = (*tri_sf_)[ki];
+	  Vector3D xyz1 = curr->getPoint();
+	  vector<ftSamplePoint*> adj = curr->getNeighbours();
+	  if (adj.size() > 0)
+	    {
+	      double fac2 = 1.0/(double)adj.size();
+	      for (size_t kj=0; kj<adj.size(); ++kj)
+		{
+		  Vector3D xyz2 = adj[kj]->getPoint();
+		  tmp_len += fac2*xyz1.dist(xyz2);
+		}
+	    }
+	  mean_edge_len_ += fac1*tmp_len;
+	}
+    }
+	      
+	  
+  
+  // Set default parameters
+  model_character_ = ROUGH;
+  initParameters();
+  max_next_ = std::min(80, tri_sf_->size()/200);
+  max_next_ = std::max(2*min_next_, max_next_);
+}
+
+
+//===========================================================================
+RevEng::RevEng()
+//===========================================================================
+{
+  // Empty infrastructure for reading stage
+  model_character_ = ROUGH;
+  initParameters();
+}
+
+
+//===========================================================================
+RevEng::~RevEng()
+//===========================================================================
+{
+}
+
+
+int compare_x_par(const RevEngPoint* p1, const RevEngPoint* p2)
+{
+  return (p1->getPoint()[0] < p2->getPoint()[0]);
+  // if (p1->getPoint()[0] < p2->getPoint()[0])
+  //   return -1;
+  // else if (p1->getPoint()[0] > p2->getPoint()[0])
+  //   return 1;
+  // else
+  //   return 0;
+}
+
+int compare_y_par(const RevEngPoint* p1, const RevEngPoint* p2)
+{
+  return (p1->getPoint()[1] < p2->getPoint()[1]);
+  // if (p1->getPoint()[1] < p2->getPoint()[1])
+  //   return -1;
+  // else if (p1->getPoint()[1] > p2->getPoint()[1])
+  //   return 1;
+  // else
+  //   return 0;
+}
+
+int compare_z_par(const RevEngPoint* p1, const RevEngPoint* p2)
+{
+  return (p1->getPoint()[2] < p2->getPoint()[2]);
+  // if (p1->getPoint()[2] < p2->getPoint()[2])
+  //   return -1;
+  // else if (p1->getPoint()[2] > p2->getPoint()[2])
+  //   return 1;
+  // else
+  //   return 0;
+}
+
+//===========================================================================
+void RevEng::setBoundingBox()
+//===========================================================================
+{
+  int nmbpt = tri_sf_->size();
+  vector<RevEngPoint*> all_pts(nmbpt);
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      RevEngPoint* pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      Vector3D xyz = pt->getPoint();
+      Point xyz2 = Point(xyz[0], xyz[1], xyz[2]);
+      all_pts[ki] = pt;
+      if (ki == 0)
+	bbox_ = BoundingBox(xyz2, xyz2);
+      else
+	bbox_.addUnionWith(xyz2);
+    }
+
+}
+
+//===========================================================================
+void RevEng::enhancePoints()
+//===========================================================================
+{
+  setBoundingBox();
+#ifdef DEBUG_ENHANCE  
+  std::cout << "Bounding box, min: " << bbox_.low() << ", max: " << bbox_.high() << std::endl;
+#endif
+
+  // Update parameters based on surface roughness
+  updateParameters();
+  int nmbpt = tri_sf_->size();
+
+#ifdef DEBUG_TRIANG
+  vector<vector<RevEngPoint*> > conn_groups;
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      if (pt->visited())
+	continue;
+      vector<RevEngPoint*> curr_group;
+      pt->fetchConnected(0, nmbpt, curr_group);
+      conn_groups.push_back(curr_group);
+    }
+  
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      pt->unsetVisited();
+    }
+
+  std::ofstream oftri("init_ptgroups.g2");
+  for (size_t kj=0; kj<conn_groups.size(); ++kj)
+    {
+      oftri << "400 1 0 0" << std::endl;
+      oftri << conn_groups[kj].size() << std::endl;
+      for (size_t kr=0; kr<conn_groups[kj].size(); ++kr)
+	oftri << conn_groups[kj][kr]->getPoint() << std::endl;
+    }
+#endif
+  
+#ifdef DEBUG_DIV
+  int writepoints = 0;
+  vector<double> tri_ang(nmbpt);
+#endif
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+
+      // Compute surface normal from triangulation
+      pt->computeTriangNormal(100.0*mean_edge_len_);
+      if (pt->getNmbNeighbour() == 0)
+	pt->setOutlier();
+      //double avlen = pt->getMeanEdgLen();
+#ifdef DEBUG_DIV
+      tri_ang[ki] = pt->getTriangAngle();
+#endif
+    }
+#ifdef DEBUG_ENHANCE  
+  std::sort(tri_ang.begin(), tri_ang.end());
+  std::cout << "Triangle angles: " << tri_ang[0] << " " << tri_ang[nmbpt/4];
+  std::cout << " " << tri_ang[nmbpt/2] << " " << tri_ang[3*nmbpt/4];
+  std::cout << " " << tri_ang[nmbpt-1] << std::endl;
+  std::cout << "norm_ang_lim_ : " << norm_ang_lim_ << std::endl;
+#endif
+  
+  double wgt_nmb = 1.0/(double)nmbpt;
+  double av_close = 0.0;
+  int max_close = 0;
+  int min_close = nmbpt;
+  vector<double> lambda_3(nmbpt);
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      if (pt->nmbLocFunc() > 0)
+	continue;  // Already enhanced
+
+      // // Compute surface normal from triangulation
+      // pt->computeTriangNormal(100.0*mean_edge_len_);
+      if (pt->isOutlier())
+	continue;
+
+      // if (pt->getNmbNeighbour() == 0)
+      // 	{
+      // 	  pt->setOutlier();
+      // 	  continue;
+      // 	}
+
+      //double avlen = pt->getMeanEdgLen();
+
+      //Fetch nearby points
+      vector<RevEngPoint*> nearpts;
+      double local_len = pt->getMeanEdgLen(10.0*mean_edge_len_);
+      double radius = rfac_*(local_len + mean_edge_len_);
+      double radius2 = 0.5*radius;
+      radius = std::min(radius, 20.0*mean_edge_len_);
+      //radius *= 1.5; // TEST 
+      //double radius = 0.5*rfac_*(local_len + mean_edge_len_);
+      pt->fetchClosePoints2(radius, min_next_, max_next_, nearpts);
+
+      Point mincvec(0.0, 0.0, 0.0), maxcvec(0.0, 0.0, 0.0);
+      //      Point mincvec2(0.0, 0.0, 0.0), maxcvec2(0.0, 0.0, 0.0);
+
+      av_close += wgt_nmb*(double)nearpts.size();
+      max_close = std::max(max_close, (int)nearpts.size());
+      min_close = std::min(min_close, (int)nearpts.size());
+      
+      if (nearpts.size() >= 3)
+	{
+#ifdef DEBUG_DIV
+	  std::ofstream of("nearpts.g2");
+	  if (writepoints)
+	    {
+	      of << "400 1 0 4 255 0 0 255" << std::endl;
+	      of << "1" << std::endl;
+	      of << pt->getPoint() << std::endl << std::endl;
+	      of << "400 1 0 4 0 255 0 255" << std::endl;
+	      of << nearpts.size() << std::endl;
+	      for (size_t kr=0; kr<nearpts.size(); ++kr)
+		of << nearpts[kr]->getPoint() << std::endl;
+	    }
+#endif
+	  
+	  // Compute eigenvectors and values of covariance matrix
+	  nearpts.push_back(pt);
+	  double lambda[3];
+	  double eigenvec[3][3];
+	  RevEngUtils::principalAnalysis(nearpts, lambda, eigenvec);
+	  Point eigen1(eigenvec[0][0], eigenvec[0][1], eigenvec[0][2]);
+	  Point eigen2(eigenvec[1][0], eigenvec[1][1], eigenvec[1][2]);
+	  Point eigen3(eigenvec[2][0], eigenvec[2][1], eigenvec[2][2]);
+	  lambda_3[ki] = lambda[2];
+	  Point tnorm = pt->getTriangNormal();
+	  if (tnorm.length() < 1.0e-10)
+	    {
+	      int stop_norm = 1;
+	    }
+	  else if (eigen3*tnorm <  0.0)
+	    {
+	      eigen2 *= -1;
+	      eigen3 *= -1;
+	    }
+
+	  for (size_t kr=0; kr<nearpts.size(); ++kr)
+	    {
+	      if (pt->pntDist(nearpts[kr]) <= radius2)
+		nearpts[kr]->addCovarianceEigen(eigen1, lambda[0], eigen2, lambda[1],
+						eigen3, lambda[2]);
+	    }
+#ifdef DEBUG_DIV      
+	  if (writepoints)
+	    {
+	      for (int ki=0; ki<3; ++ki)
+		{
+		  Vector3D vec(eigenvec[ki][0], eigenvec[ki][1], eigenvec[ki][2]);
+		  of << "410 1 0 4 0 0 0 255" << std::endl;
+		  of << "1" << std::endl;
+		  Vector3D curr = pt->getPoint();
+		  of << curr << " " << curr+0.1*vec << std::endl;
+		}
+	    }
+#endif
+	  // Compute normal and curvature using LocFunc patch
+	  // Point normal;//, mincvec, maxcvec;
+	  // double minc, maxc;
+	  // double currdist, avdist;
+	  // RevEngUtils::computeLocFunc(curr, nearpts, eigen1, eigen3, normal, mincvec, minc,
+	  // 				maxcvec, maxc, currdist, avdist);
+	  computeLocFunc(pt, nearpts, eigen1, eigen3, radius2);
+	  // Orient vectors with respect to triangulation normal
+	  // The normal vectors should be OK. Curvature vectors are not necessarily
+	  // consistent with regard to orientation
+
+	  int stop_break = 1;
+	}
+    }
+
+#ifdef DEBUG
+  vector<Vector3D> pts1;
+  vector<Vector3D> lin1;
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]); 
+      if (!pt->isOutlier())
+	{
+	  pts1.push_back(pt->getPoint());
+	  vector<ftSamplePoint*> next = pt->getNeighbours();
+	  for (size_t kj=0; kj<next.size(); ++kj)
+	    {
+	      RevEngPoint *pt2 = dynamic_cast<RevEngPoint*>(next[kj]);
+	      if (!pt2->isOutlier())
+		{
+		  lin1.push_back(pt->getPoint());
+		  lin1.push_back(pt2->getPoint());
+		}
+	    }
+	}
+    }
+  std::ofstream ofout1("not_outliers.g2");
+  ofout1 << "400 1 0 4 0 0 0 255" << std::endl;
+  ofout1 << pts1.size() << std::endl;
+  for (size_t kj=0; kj<pts1.size(); ++kj)
+    ofout1 << pts1[kj] << std::endl;
+  ofout1 << "410 1 0 4 55 100 100 255" << std::endl;
+  ofout1 << lin1.size()/2 << std::endl;
+  for (size_t kj=0; kj<lin1.size(); kj+=2)
+    ofout1 << lin1[kj] << " " << lin1[kj+1] << std::endl;
+#endif
+  std::sort(lambda_3.begin(), lambda_3.end());
+#ifdef DEBUG_ENHANCE  
+  std::cout << "No close, min: " << min_close << ", max: " << max_close << ", average: " << av_close << std::endl;
+  std::cout << "lambda3, min: " << lambda_3[0] << ", max: " << lambda_3[nmbpt-1] << ", medium: " << lambda_3[nmbpt/2] << std::endl;
+#endif
+
+
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      // Check orientation of curvature
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]); 
+      if (pt->isOutlier())
+	continue;
+      Point tnorm = pt->getTriangNormal();
+      if (tnorm.length() < 1.0e-10)
+	{
+	  // Fetch triangulation normal in neighbour
+	  vector<ftSamplePoint*> next = pt->getNeighbours();
+	  double mindist = std::numeric_limits<double>::max();
+	  int ix = -1;
+	  for (size_t kr=0; kr<next.size(); ++kr)
+	    {
+	      double dist = pt->pntDist(next[kr]);
+	      RevEngPoint *nextpt = dynamic_cast<RevEngPoint*>(next[kr]);
+	      Point nextnorm = nextpt->getTriangNormal();
+	      if (dist < mindist && nextnorm.length() > 1.0e-10)
+		{
+		  mindist = dist;
+		  ix = (int)kr;
+		}
+	    }
+	  if (ix >= 0)
+	    {
+	      RevEngPoint *nextpt = dynamic_cast<RevEngPoint*>(next[ix]);
+	      tnorm = nextpt->getTriangNormal();
+	      Point PCAnorm = pt->getPCANormal();
+	      if (tnorm*PCAnorm < 0.0)
+		pt->turnPCA();
+	      Point LocFuncnorm = pt->getLocFuncNormal();
+	      if (tnorm*LocFuncnorm < 0.0)
+		pt->turnLocFuncNorm();
+	    }
+	  
+	}
+    }
+  
+
+#ifdef DEBUG_ENHANCE  
+  std::cout << "Start curvature filter" << std::endl;
+#endif
+  curvatureFilter();
+  
+#ifdef DEBUG_ENHANCE  
+  std::cout << "Finish curvature filter" << std::endl;
+#endif 
+  int stop_break = 1;
+
+}
+
+//===========================================================================
+void RevEng::computeLocFunc(RevEngPoint* pt, std::vector<RevEngPoint*>& points,
+			  Point& vec1, Point& vec2, double radius)
+//===========================================================================
+{
+  // Transform points to coordinate system given by vec1 (x-axis) and vec2 (y-axis)
+  Matrix3D mat1, mat2, rotmat;
+  Vector3D vec1_2(vec1[0], vec1[1], vec1[2]);
+  Vector3D vec2_2(vec2[0], vec2[1], vec2[2]);
+  Vector3D xaxis(1, 0, 0);
+  Vector3D zaxis(0, 0, 1);
+  mat1.setToRotation(vec1_2, xaxis);
+  Vector3D v1 = mat1*vec1_2;
+  Vector3D vec2_3 = mat1*vec2_2;
+  mat2.setToRotation(vec2_3, zaxis);
+  Vector3D v2 = mat2*vec2_3;
+  rotmat = mat2*mat1;
+  //rotmat.identity();
+
+  // Perform rotation and sort parameter values and z-value
+  int nmbpts = (int)points.size();
+  vector<double> par(2*nmbpts);
+  vector<double> zval(nmbpts);
+  Vector3D curr = pt->getPoint();
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      Vector3D dv = points[ki]->getPoint() - curr;
+      Vector3D dvrot = rotmat*dv;
+      //Vector3D dvrot = mat2*dvrot0;
+      par[2*ki] = curr[0] + dvrot[0];
+      par[2*ki+1] = curr[1] + dvrot[1];
+      zval[ki] = curr[2] + dvrot[2];
+    }
+
+  // Approximate z-component by biquadratic Bezier function in x and y
+  int order = 3;
+  shared_ptr<SplineSurface> mongesf = RevEngUtils::surfApprox(zval, 1, par, order,
+							      order, order, order);
+
+  vector<double> coefs2(3*order*order);
+  std::vector<double>::iterator cf = mongesf->coefs_begin();
+  for (int ka=0; ka<order; ++ka)
+    {
+      double vpar = mongesf->basis_v().grevilleParameter(ka);
+      for (int kb=0; kb<order; ++kb, ++cf)
+	{
+	  double upar = mongesf->basis_u().grevilleParameter(kb);
+	  coefs2[(ka*order+kb)*3] = upar;
+	  coefs2[(ka*order+kb)*3+1] = vpar;
+	  coefs2[(ka*order+kb)*3+2] = *cf;
+	}
+    }
+  shared_ptr<SplineSurface> tmp(new SplineSurface(order, order, order, order, 
+						  mongesf->basis_u().begin(),
+						  mongesf->basis_v().begin(), &coefs2[0], 3));
+#ifdef DEBUG_DIV
+  int writesurface = 0;
+  if (writesurface)
+    {
+      std::ofstream of("approx_sf.g2");
+      tmp->writeStandardHeader(of);
+      tmp->write(of);
+      of << "400 1 0 4 0 255 0 255" << std::endl;
+      of << 1 << std::endl;
+      of << curr << std::endl;
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of << nmbpts << std::endl;
+      for (int ka=0; ka<nmbpts; ++ka)
+	{
+	  Point tmppt(par[2*ka], par[2*ka+1], zval[ka]);
+	  of << tmppt << std::endl;
+	}
+    }
+#endif
+  
+  // Compute surface normal 
+  double avdist = 0.0;
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      Point pos;
+      mongesf->point(pos, par[2*ki], par[2*ki+1]);
+      avdist += fabs(zval[ki] - pos[0]);
+    }
+  avdist /= (double)nmbpts;
+
+#ifdef DEBUG_MONGE
+  std::ofstream of2("LocFunc_curvature.g2");
+  std::ofstream of3("LocFunc_curvature2.g2");
+#endif
+  vector<Point> monge1, monge2, monge3, monge4;
+   for (size_t kr=0; kr<points.size(); ++kr)
+    {
+      if (pt->pntDist(points[kr]) > radius)
+	continue;
+
+      Point triang_norm = points[kr]->getTriangNormal();
+      vector<Point> der(3);
+      mongesf->point(der, par[2*kr], par[2*kr+1], 1);
+      Vector3D norm(-der[1][0], -der[2][0], 1.0);
+      norm.normalize();
+
+      // Accuracy of approximation
+      double currdist = fabs(zval[kr] - der[0][0]);
+  
+      // Compute principal curvatures in curr
+      SISLSurf *sislsf = GoSurf2SISL(*mongesf, false);
+      int left1 = 0, left2 = 0;
+      int stat = 0;
+      double minc, maxc;
+      double d1[2], d2[2];
+      s2542(sislsf, 0, 0, 0, &par[0], &left1, &left2, &minc, &maxc, d1, d2, &stat);
+      Vector3D du(1.0, 0.0, der[1][0]);
+      Vector3D dv(0.0, 1.0, der[2][0]);
+      Vector3D cvec1 = d1[0]*du + d1[1]*dv;
+      Vector3D cvec2 = d2[0]*du + d2[1]*dv;
+      if (sislsf) freeSurf(sislsf);
+
+      // Vector3D origin(par[0], par[1], zval[0]);
+      // of << "410 1 0 4 0 0 0 255" << std::endl;
+      // of << "1" << std::endl;
+      // of << origin << " " << origin+norm << std::endl;
+
+      // of << "410 1 0 4 0 55 155 255" << std::endl;
+      // of << "1" << std::endl;
+      // of << origin << " " << origin+cvec1 << std::endl;
+
+  
+      // of << "410 1 0 4 155 55 0 255" << std::endl;
+      // of << "1" << std::endl;
+      // of << origin << " " << origin+cvec2 << std::endl;
+
+  
+  
+      // Transform results to original coordinate system
+      Matrix3D mat3, mat4, rotmat2;
+      mat4.setToRotation(zaxis, vec2_3);
+      mat3.setToRotation(xaxis, vec1_2);
+      rotmat2 = mat3*mat4;
+      //rotmat2.identity();
+      //Vector3D norm0 = mat4*norm;
+      Vector3D norm2 = rotmat2*norm;
+      Point normal = Point(norm2[0], norm2[1], norm2[2]);
+      if (triang_norm.length() > 1.0e-10 && normal*triang_norm < 0.0)
+	normal *= -1;
+  
+      Vector3D cvec3 = rotmat2*cvec1;
+      Point mincvec = Point(cvec3[0], cvec3[1], cvec3[2]); 
+      Vector3D cvec4 = rotmat2*cvec2;
+      Point maxcvec = Point(cvec4[0], cvec4[1], cvec4[2]);
+      points[kr]->addLocFuncInfo(normal, mincvec, minc, maxcvec, maxc, currdist, avdist);
+
+      // Vector3D xyz = points[kr]->getPoint();
+      // Point xyz2 = Point(xyz[0], xyz[1], xyz[2]);
+      // Vector3D der2(der[0][0], der[0][1], der[0][2]);
+      // Vector3D der3 = rotmat2*der2;
+      // Point der4(der3[0], der3[1], der3[2]);  // Not a 3D point!!!
+      // monge1.push_back(xyz2);
+      // monge1.push_back(xyz2+mincvec);
+      // monge2.push_back(xyz2);
+      // monge2.push_back(xyz2+maxcvec);
+      // monge3.push_back(der4);
+      // monge3.push_back(der4+mincvec);
+      // monge4.push_back(der4);
+      // monge4.push_back(der4+maxcvec);
+    }
+
+   // int writeLocFunc = 0;
+
+   // if (writeLocFunc)
+   //   {
+   //     of2 << "410 1 0 4 255 0 0 255" << std::endl;
+   //     of2 << monge1.size()/2 << std::endl;
+   //     for (size_t kr=0; kr<monge1.size(); kr+=2)
+   // 	 of2 << monge1[kr] << " " << monge1[kr+1] << std::endl;
+   //     of2 << "410 1 0 4 0 0 255 255" << std::endl;
+   //     of2 << monge2.size()/2 << std::endl;
+   //     for (size_t kr=0; kr<monge2.size(); kr+=2)
+   // 	 of2 << monge2[kr] << " " << monge2[kr+1] << std::endl;
+   
+   //     of3 << "400 1 0 4 0 255 0 255" << std::endl;
+   //     of3 << monge3.size()/2 << std::endl;
+   //     for (size_t kr=0; kr<monge3.size(); kr+=2)
+   // 	 of3 << monge3[kr] << std::endl;
+   //     of3 << "410 1 0 4 255 0 0 255" << std::endl;
+   //     of3 << monge3.size()/2 << std::endl;
+   //     for (size_t kr=0; kr<monge3.size(); kr+=2)
+   // 	 of3 << monge3[kr] << " " << monge3[kr+1] << std::endl;
+   //     of3 << "410 1 0 4 0 0 255 255" << std::endl;
+   //     of3 << monge4.size()/2 << std::endl;
+   //     for (size_t kr=0; kr<monge4.size(); kr+=2)
+   // 	 of3 << monge4[kr] << " " << monge4[kr+1] << std::endl;
+   //   }
+  int stop_break = 1;
+}
+ 
+
+//===========================================================================
+void RevEng::curvatureFilter()
+//===========================================================================
+{
+  int nmbpt = tri_sf_->size();
+  double radius_fac = 0.2; //0.7;
+  vector<vector<RevEngPoint*> > nearpts(nmbpt);
+  bool smoothcurv = true; //false;
+  if (smoothcurv)
+    {
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      
+      // Fetch nearby points
+      double local_len = pt->getMeanEdgLen();
+      double radius = 0.5*radius_fac*rfac_*(local_len + mean_edge_len_);
+      radius = std::min(radius, 20.0*mean_edge_len_);
+      pt->fetchClosePoints2(radius, min_next_/2, max_next_/2, nearpts[ki]);
+      if (nearpts[ki].size() == 0)
+	continue;
+      vector<double> H0(nearpts[ki].size()+1);
+      vector<double> K0(nearpts[ki].size()+1);
+      H0[0] = pt->meanCurvature0();
+      K0[0] = pt->GaussCurvature0();
+      for (size_t kj=0; kj<nearpts[ki].size(); ++kj)
+	{
+	  H0[kj+1] = nearpts[ki][kj]->meanCurvature0();
+	  K0[kj+1] = nearpts[ki][kj]->GaussCurvature0();
+	}
+      std::sort(H0.begin(), H0.end());
+      std::sort(K0.begin(), K0.end());
+      pt->setMeanCurvature(0.5*H0[H0.size()/2] + H0[(H0.size()+1)/2]);
+      pt->setGaussCurvature(0.5*K0[K0.size()/2] + K0[(K0.size()+1)/2]);
+    }
+  
+  for (int ki=0; ki<nmbpt; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      pt->updateCurvature();
+    }
+
+  int nmbsmooth = (model_character_ <= MEDIUM_ROUGH) ? 2 : 5;
+  for (int ka=0; ka<nmbsmooth; ++ka)
+    {
+      for (int ki=0; ki<nmbpt; ++ki)
+	{
+	  RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      
+	  // Fetch nearby points
+	  double Hmean = pt->meanCurvature0();
+	  double Kmean = pt->GaussCurvature0();
+	  for (size_t kj=0; kj<nearpts[ki].size(); ++kj)
+	    {
+	      Hmean += nearpts[ki][kj]->meanCurvature0();
+	      Kmean += nearpts[ki][kj]->GaussCurvature0();
+	    }
+	  Hmean /= (double)(nearpts[ki].size()+1);
+	  Kmean /= (double)(nearpts[ki].size()+1);
+	  pt->setMeanCurvature(Hmean);
+	  pt->setGaussCurvature(Kmean);
+	}
+      for (int ki=0; ki<nmbpt; ++ki)
+	{
+	  RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+	  pt->updateCurvature();
+	}
+   }
+    }
+}
+
+
+//===========================================================================
+void RevEng::edgeClassification()
+//===========================================================================
+{
+  int nmbpts = tri_sf_->size();
+  vector<Vector3D> triangcorners;
+  vector<Vector3D> curvaturecorners;
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      Vector3D xyz = pt->getPoint();
+      
+      if (pt->getTriangAngle() > norm_ang_lim_)
+	triangcorners.push_back(xyz);
+      
+       // Curvature edge classification
+      double avlen = pt->getMeanEdgLen();
+      double maxpc = std::max(fabs(pt->maxPrincipalCurvature()),
+			      fabs(pt->minPrincipalCurvature()));
+      double crvrad = 1.0/maxpc; 
+      int c1_edge = (crvrad < cfac_*avlen) ? C1_EDGE : C1_NOT_EDGE;
+      if (c1_edge == C1_EDGE)
+	curvaturecorners.push_back(xyz);
+
+
+      // Store classification in point
+      pt->setEdgeClassification(c1_edge);
+    }
+  
+  // Specify/clean edge classification
+  bool closeedge = false;
+  int nmbedge = 2;
+  for (int ka=0; ka<nmbpts; ++ka)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ka]);
+      if (pt->isEdge(edge_class_type_))
+	{
+	  if (pt->isolatedEdge(edge_class_type_, nmbedge, closeedge))
+	    pt->setEdgeUndef();
+
+	  else 
+	    // If the angular difference between triangle normals is less
+	    // then the limit, classify the point as CLOSE_EDGE.
+	    pt->adjustWithTriangNorm(norm_ang_lim_);
+	}
+    }
+  
+  vector<Vector3D> edgepts;
+  for (int ka=0; ka<nmbpts; ++ka)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ka]);
+      if (pt->isEdge(edge_class_type_))
+	edgepts.push_back(pt->getPoint());
+    }
+
+
+#ifdef DEBUG_ENHANCE
+  std::ofstream of2("triangcorners.g2");
+  of2 << "400 1 0 4 0 0 0 255" << std::endl;
+  of2 << triangcorners.size() << std::endl;
+  for (size_t kj=0; kj<triangcorners.size(); ++kj)
+    of2 << triangcorners[kj] << std::endl;
+
+  std::ofstream of3("curvaturecorners.g2");
+  of3 << "400 1 0 4 10 10 10 255" << std::endl;
+  of3 << curvaturecorners.size() << std::endl;
+  for (size_t kj=0; kj<curvaturecorners.size(); ++kj)
+    of3 << curvaturecorners[kj] << std::endl;
+#endif
+
+#ifdef DEBUG_EDGE0
+   if (edgepts.size() > 0)
+    {
+      std::ofstream ofedg("edgepts.g2");
+      ofedg << "400 1 0 4 255 0 0 255" << std::endl;
+      ofedg << edgepts.size() << std::endl;
+      for (size_t kr=0; kr<edgepts.size();++kr)
+	ofedg << edgepts[kr] << std::endl;
+    }
+#endif
+   int stop_break = 1;
+ }
+
+//===========================================================================
+void RevEng::classifyPoints()
+//===========================================================================
+{
+  // First extract obvious edges
+  edgeClassification();
+  
+  // Fetch relevant values for all points
+  vector<vector<Vector3D> > class_pts(9);
+  int nmbpts = tri_sf_->size();
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      Vector3D xyz = pt->getPoint();
+
+      // Curvature surface classification
+      int ctype = C1_UNDEF;
+      double gausscurv = pt->GaussCurvature();
+      double meancurv = pt->meanCurvature();
+      if (meancurv < -zero_H_)
+	{
+	  if (gausscurv > zero_K_)
+	    {
+	      ctype = C1_PEAK;
+	      class_pts[0].push_back(xyz);
+	    }
+	  else if (gausscurv < -zero_K_)
+	    {
+	      ctype = C1_SRIDGE;
+	      class_pts[2].push_back(xyz);
+	    }
+	  else
+	    {
+	      ctype = C1_RIDGE;
+	      class_pts[1].push_back(xyz);
+	    }
+	}
+      else if (meancurv > zero_H_)
+	{
+	  if (gausscurv > zero_K_)
+	    {
+	      ctype = C1_PIT;
+	      class_pts[6].push_back(xyz);
+	    }
+	  else if (gausscurv < -zero_K_)
+	    {
+	      ctype = C1_SVALLEY;
+	      class_pts[8].push_back(xyz);
+	    }
+	  else
+	    {
+	      ctype = C1_VALLEY;
+	      class_pts[7].push_back(xyz);
+	    }
+	}
+      else
+	{
+	  if (gausscurv > zero_K_)
+	    {
+	      ctype = C1_NONE;
+	      class_pts[3].push_back(xyz);
+	    }
+	  else if (gausscurv < -zero_K_)
+	    {
+	      ctype = C1_MINSURF;
+	      class_pts[5].push_back(xyz);
+	    }
+	  else
+	    {
+	      ctype = C1_FLAT;
+	      class_pts[4].push_back(xyz);
+	    }
+	}
+
+
+
+      // Store classification in point
+      pt->setClassification(ctype);
+   }
+
+#ifdef DEBUG_SEG
+  std::ofstream of("curvature_segments.g2");
+  for (int ka=0; ka<3; ++ka)
+    for (int kb=0; kb<3; ++kb)
+      {
+	of << "400 1 0 4 ";
+	for (int kc=0; kc<3; ++kc)
+	  of << colors[3*ka+kb][kc] << " ";
+	of << "255" << std::endl;
+	of << class_pts[3*ka+kb].size() << std::endl;
+	for (size_t kr=0; kr<class_pts[3*ka+kb].size(); ++kr)
+	  of << class_pts[3*ka+kb][kr] << std::endl;
+      }
+
+
+ #endif  
+  int stop_break = 1;
+}
+
+struct
+{
+  bool operator()(shared_ptr<RevEngRegion> a, shared_ptr<RevEngRegion> b)
+  {
+    return (a->numPoints() > b->numPoints());
+    //   return 1;
+    // else if (a->numPoints() == b->numPoints())
+    //   return 2;
+    // else
+    //   return 3;
+  }
+} sort_region;
+
+//===========================================================================
+void RevEng::setApproxTolerance()
+//===========================================================================
+{
+  double eps = getInitApproxTol();
+#ifdef DEBUG
+  std::cout << "Approx tol: " << eps << std::endl;
+#endif
+  // std::cout << "New tolerance: " << std::endl;
+  // std::cin >> eps;
+  setApproxTol(eps);
+}
+
+//===========================================================================
+double RevEng::getInitApproxTol()
+//===========================================================================
+{
+  int nmbpts = tri_sf_->size();
+  vector<double> pointdist;
+  double maxdist = 0.0;
+  double avptdist = 0.0;
+  double minptdist = std::numeric_limits<double>::max();
+  double maxptdist = 0.0;
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      if (!pt->isEdge(edge_class_type_))
+	{
+	  double ptdist = pt->getPointDistance();
+	  pointdist.push_back(ptdist);
+	  minptdist = std::min(minptdist, ptdist);
+	  maxptdist = std::max(maxptdist, ptdist);
+	  avptdist += ptdist;
+	}
+    }
+  if (pointdist.size() > 0)
+    avptdist /= (double)pointdist.size();
+  
+  std::sort(pointdist.begin(), pointdist.end());
+  
+  std::sort(pointdist.begin(), pointdist.end());
+  double dlim = 0.93; //0.75;
+  int dix = (int)(dlim*(double)pointdist.size());
+  double eps = pointdist[dix];
+  if (model_character_ == MEDIUM_ROUGH)
+    eps *= 1.5;
+  else if (model_character_ == ROUGH)
+    eps *= 2.0;
+
+  // Just to test
+  //eps = 0.1;
+
+#ifdef DEBUG_DIV
+  std::cout << "Maxptdist: " << maxdist << ", avdist: " << avptdist;
+  std::cout << ", medptdist: " << pointdist[pointdist.size()/2];
+  std::cout << ", eps: " << eps << std::endl;
+  
+  std::ofstream ofd("ptdist.g2");
+#endif
+  double ptd1 = minptdist;
+  double ptd2 = maxptdist;
+  int nmbd = 12;
+  double pdel = (ptd2 - ptd1)/(double)nmbd;
+  vector<vector<Vector3D> > ptrs(nmbd);
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      Vector3D xyz = pt->getPoint();
+      double dd = pt->getPointDistance();
+      int ix = (int)((dd-ptd1)/pdel);
+      ix = std::min(ix, nmbd-1);
+      ptrs[ix].push_back(xyz);
+    }
+
+#ifdef DEBUG_DIV
+  for (int ki=0; ki<nmbd; ++ki)
+    {
+      if (ptrs[ki].size() > 0)
+	{
+	  ofd << "400 1 0 4 " << colors[ki][0] << " " << colors[ki][1] << " ";
+	  ofd << colors[ki][2] << " 255"  << std::endl;
+	  ofd << ptrs[ki].size();
+	  for (size_t kr=0; kr<ptrs[ki].size(); ++kr)
+	    ofd << ptrs[ki][kr] << std::endl;
+	}
+    }
+#endif
+
+  return eps;
+ }
+
+//===========================================================================
+void RevEng::segmentIntoRegions()
+//===========================================================================
+{
+  // Collect continous regions
+  //int min_point_in = 50; //10; //20;  // Should be set depending on the total
+  // number of points. Probably class parameter. Need to look at the use
+  int nmbpts = tri_sf_->size();
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      if (pt->hasRegion())
+	continue;
+      if (pt->closeEdge(edge_class_type_))
+	continue;
+
+      if (pt->nmbSameClassification(classification_type_) == 0)
+	single_points_.push_back(pt);
+      else
+	{
+	  shared_ptr<RevEngRegion> region(new RevEngRegion(classification_type_,
+							   edge_class_type_));
+	  pt->setRegion(region.get());
+	  region->collect(pt);
+	  if (region->numPoints() == 1)
+	    {
+	      RevEngPoint *pt_single = region->getPoint(0);
+	      pt_single->unsetRegion();
+	      single_points_.push_back(pt_single);
+	    }
+	  else
+	    regions_.push_back(region);
+	}
+    }
+
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+#ifdef DEBUG
+  if (regions_.size() > 0)
+    {
+      std::cout << "Regions 1" << std::endl;
+      std::ofstream of("regions1.g2");
+      std::ofstream ofm("mid_regions1.g2");
+      std::ofstream ofs("small_regions1.g2");
+      writeRegionStage(of, ofm, ofs);
+    }
+#endif
+
+  // Sort regions according to number of points
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+
+  min_point_region_ = setSmallRegionNumber();
+#ifdef DEBUG
+  std::cout << "Min point region: " << min_point_region_ << std::endl;
+#endif
+#ifdef DEBUG_PLANAR
+  std::ofstream ofpc("cand_planar.g2");
+#endif  
+  double lim_cone = 0.1*M_PI;
+  size_t numreg = regions_.size();
+  for (size_t ki=0; ki<numreg; ++ki)
+    {
+      double avH, avK, MAH, MAK;
+      regions_[ki]->getAvCurvatureInfo(avH, avK, MAH, MAK);
+      if (regions_[ki]->numPoints() > min_point_region_ &&
+	  (regions_[ki]->planartype() || MAH <= zero_H_) && 
+	  (!regions_[ki]->feasiblePlane(zero_H_, zero_K_)))
+	{
+#ifdef DEBUG_PLANAR
+	  regions_[ki]->writeRegionPoints(ofpc);
+#endif
+	  vector<vector<RevEngPoint*> > other_groups;
+	  vector<RevEngPoint*> single;
+	  regions_[ki]->splitPlanar(lim_cone, min_point_region_/2, other_groups, single);
+	  if (other_groups.size() > 0)
+	    {
+	      vector<HedgeSurface*> dummy;
+	      surfaceExtractOutput((int)ki, other_groups, dummy);
+	    }
+	  if (single.size())
+	    single_points_.insert(single_points_.end(), single.begin(), single.end());
+
+	  int stop_cand_plane = 1;
+	}
+    }
+  
+  // Set adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+#ifdef DEBUG_PLANAR
+  std::ofstream ofp("planar_reg.g2");
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      if (regions_[ki]->feasiblePlane(zero_H_, zero_K_))
+	regions_[ki]->writeRegionPoints(ofp);
+    }
+#endif
+  // Integrate single points when appropriate
+  vector<RevEngPoint*> remaining_single;
+  for (int ka=0; ka<(int)single_points_.size(); ++ka)
+    {
+      bool merged = single_points_[ka]->mergeWithAdjacent(mean_edge_len_);
+      if (!merged)
+	{
+	  single_points_[ka]->setOutlier();
+	  remaining_single.push_back(single_points_[ka]);
+	}
+    }
+  std::swap(single_points_, remaining_single);
+
+  // Merge adjacent planar regions
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      if (regions_[ki]->feasiblePlane(zero_H_, zero_K_))
+	{
+	  vector<RevEngRegion*> grown_regions;
+	  vector<HedgeSurface*> adj_surfs;
+	  bool merged = regions_[ki]->mergePlanarReg(zero_H_, zero_K_, 
+						     approx_tol_, mainaxis_,
+						     grown_regions);
+	  if (merged)
+	    {
+	      if (grown_regions.size() > 0 || adj_surfs.size() > 0)
+		updateRegionsAndSurfaces(ki, grown_regions, adj_surfs);
+	      regions_[ki]->updateRegionAdjacency();
+	    }
+	}
+    }
+  
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+  
+#ifdef DEBUG
+  checkConsistence("Regions1_2");
+
+  if (regions_.size() > 0)
+    {
+      std::cout << "Regions 1_2" << std::endl;
+      std::ofstream of("regions1_2.g2");
+      std::ofstream ofm("mid_regions1_2.g2");
+      std::ofstream ofs("small_regions1_2.g2");
+      writeRegionStage(of, ofm, ofs);
+     }
+#endif
+  
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+  
+  // Simplify regions structure
+#ifdef DEBUG
+  std::cout << "Number of regions pre integrate: " << regions_.size() << std::endl;
+#endif
+  int num_reg = (int)regions_.size();
+  for (int ka=num_reg-1; ka>=0; --ka)
+    {
+      bool to_be_removed = regions_[ka]->integrateInAdjacent(mean_edge_len_,
+							     min_next_, max_next_,
+							     approx_tol_, 0.5,
+							     max_nmb_outlier_);
+      if (to_be_removed)
+	{
+	  regions_[ka]->removeFromAdjacent();
+	  regions_[ka]->clearRegionAdjacency();
+	  if (ka < num_reg-1)
+	    std::swap(regions_[ka], regions_[num_reg-1]);
+	  num_reg--;
+	}
+    }
+  if (num_reg < (int)regions_.size())
+    regions_.erase(regions_.begin()+num_reg, regions_.end());
+#ifdef DEBUG
+  std::cout << "Number of regions post integrate: " << regions_.size() << std::endl;
+#endif  
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+  
+#ifdef DEBUG
+  checkConsistence("Regions2");
+
+  if (regions_.size() > 0)
+    {
+      std::cout << "Regions 2" << std::endl;
+      std::ofstream of("regions2.g2");
+      std::ofstream ofm("mid_regions2.g2");
+      std::ofstream ofs("small_regions2.g2");
+      writeRegionStage(of, ofm, ofs);
+     }
+#endif
+  
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+  
+  // Update minimum number of points in region for surface generation.
+  // First sort regions according to number of points
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+
+  min_point_region_ = setSmallRegionNumber();
+#ifdef DEBUG
+  std::cout << "Min point region (2): " << min_point_region_ << std::endl;
+#endif
+}
+
+
+//===========================================================================
+void RevEng::initialSurfaces()
+//===========================================================================
+{
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+  
+  // Create surfaces in simple regions (planes and cylinders) and extract
+  // deviant points
+  int min_point_in = 50; //10; //20;  // Should be set depending on the total
+  // number of points. Probably class parameter. Need to look at the use
+  double angtol = 5.0*anglim_;
+  //int regsize = (int)regions_.size();
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+#ifdef DEBUG
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      RevEngRegion *first = regions_[ki]->getPoint(0)->region();
+      int num = regions_[ki]->numPoints();
+      for (int ka=1; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != first)
+	  std::cout << "Inconsistent region pointers, pre initPlaneCyl: " << ki << " " << ka << std::endl;
+    }
+#endif
+  for (int kr=0; kr<(int)regions_.size(); ++kr)
+    {
+#ifdef DEBUG
+      std::ofstream of0("init_reg.g2");
+      regions_[kr]->writeRegionInfo(of0);
+#endif
+      
+      if (regions_[kr]->numPoints() < min_point_region_)
+	continue;
+
+      vector<vector<RevEngPoint*> > out_groups;
+      vector<RevEngPoint*> single;
+      vector<shared_ptr<HedgeSurface> > sfs;
+      vector<HedgeSurface*> prev_sfs;
+      bool repeat = false;
+      regions_[kr]->initPlaneCyl(min_point_in, min_point_region_,
+				 approx_tol_, angtol, mainaxis_,
+				 zero_H_, zero_K_, sfs, out_groups, single, repeat);
+      if (single.size() > 0)
+	single_points_.insert(single_points_.end(), single.begin(), single.end());
+      if (out_groups.size() > 0 || prev_sfs.size() > 0)
+	surfaceExtractOutput((int)kr, out_groups, prev_sfs);
+
+#ifdef DEBUG_CHECK
+      bool connect = regions_[kr]->isConnected();
+      if (!connect)
+	std::cout << "initPlaneCyl, disconnected region " << kr << std::endl;
+#endif
+      if (sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), sfs.begin(), sfs.end());
+      if (repeat)
+	--kr;
+    }
+  
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+
+#ifdef DEBUG
+  checkConsistence("Regions3");
+
+  if (regions_.size() > 0)
+    {
+      std::cout << "Regions 3" << std::endl;
+      std::ofstream of("regions3.g2");
+      std::ofstream ofm("mid_regions3.g2");
+      std::ofstream ofs("small_regions3.g2");
+      writeRegionStage(of, ofm, ofs);
+      std::ofstream of0("regions3_helix.g2");
+      for (int ki=0; ki<(int)regions_.size(); ++ki)
+	{
+	  if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+	    {
+	      regions_[ki]->writeRegionInfo(of0);
+	      if (regions_[ki]->hasSurface())
+		regions_[ki]->writeSurface(of0);
+	    }
+	}
+    }
+  
+  if (single_points_.size() > 0)
+    {
+      std::ofstream of_single("single_pts3.g2");
+      of_single << "400 1 0 4 0 0 0 255" << std::endl;
+      of_single << single_points_.size() << std::endl;
+      for (size_t kr=0; kr<single_points_.size(); ++kr)
+	of_single << single_points_[kr]->getPoint() << std::endl;
+     }
+
+  if (surfaces_.size() > 0)
+    {
+      std::ofstream of("regsurf3.g2");
+      writeRegionWithSurf(of);
+    }
+#endif
+  
+#ifdef DEBUG
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, regions 3. " << ki << " " << tmpset.size() << " " << regions_[ki]->numPoints() << std::endl;
+      int num = regions_[ki]->numPoints();
+      for (int ka=0; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+	  std::cout << "Inconsistent region pointers, post initPlaneCyl: " << ki << " " << ka << std::endl;
+    }
+#endif
+
+  // Integrate single points when appropriate
+  vector<RevEngPoint*> remaining_single;
+  for (int ka=0; ka<(int)single_points_.size(); ++ka)
+    {
+      if (single_points_[ka]->isOutlier())
+	continue;
+      bool merged = single_points_[ka]->mergeWithAdjacent(mean_edge_len_);
+      if (!merged)
+	{
+	  single_points_[ka]->setOutlier();
+	  remaining_single.push_back(single_points_[ka]);
+	}
+    }
+  std::swap(single_points_, remaining_single);
+
+  // Sort regions according to number of points
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+
+#ifdef DEBUG
+  if (surfaces_.size() > 0)
+    {
+      std::ofstream of("regsurf4_0.g2");
+      writeRegionWithSurf(of);
+    }
+#endif
+	  
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+}
+
+//===========================================================================
+void RevEng::growSurfaces()
+//===========================================================================
+{
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+  
+  //int min_point_in = 50; //10; //20;  // Should be set depending on the total
+  // number of points. Probably class parameter. Need to look at the use
+  double angtol = 5.0*anglim_;
+
+  bool joinreg = true;
+  if (joinreg)
+    {
+#ifdef DEBUG
+      std::cout << "Pre join. Number of regions: " << regions_.size() << std::endl;
+#endif
+      // int num_reg = (int)regions_.size();
+      for (int ki=0; ki<(int)regions_.size(); ++ki)
+	{
+#ifdef DEBUG
+      std::ofstream of0("init_join.g2");
+      regions_[ki]->writeRegionInfo(of0);
+#endif
+	  if (regions_[ki]->numPoints() < min_point_region_)
+	    continue;   // Grow into larger
+	  
+	  if (regions_[ki]->hasSurface())
+	    {
+	      growSurface(ki);
+#ifdef DEBUG_CHECK
+	      int num = regions_[ki]->numPoints();
+	      for (int ka=0; ka<num; ++ka)
+		if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+		  std::cout << "Inconsistent region pointers, post grow: " << ki << " " << ka << std::endl;
+#endif
+	    }
+	  // else
+	  //   {
+	  //     vector<RevEngRegion*> adapted_regions;
+	  //     regions_[ki]->joinRegions(mainaxis_, approx_tol_,
+	  // 				angtol, adapted_regions);
+	  //     for (size_t kj=0; kj<adapted_regions.size(); ++kj)
+	  // 	{
+	  // 	  size_t kr=0;
+	  // 	  for (kr=0; kr<regions_.size(); ++kr)
+	  // 	    if (adapted_regions[kj] == regions_[kr].get())
+	  // 	      break;
+
+	  // 	  if (kr < regions_.size())
+	  // 	    {
+	  // 	      // std::swap(regions_[kr], regions_[num_reg-1]);
+	  // 	      // num_reg--;
+	  // 	      regions_.erase(regions_.begin()+kr);
+	  // 	    }
+	  // 	}
+	  //   }
+#ifdef DEBUG
+      std::ofstream of02("post_join.g2");
+      regions_[ki]->writeRegionInfo(of02);
+#endif
+      int stop_grow = 1;
+	}
+      // if (num_reg < (int)regions_.size())
+      //   regions_.erase(regions_.begin()+num_reg, regions_.end());
+#ifdef DEBUG
+      std::cout << "Post join. Number of regions: " << regions_.size() << std::endl;
+
+      checkConsistence("Regions4");
+
+      if (regions_.size() > 0)
+	{
+	  std::cout << "Regions 4" << std::endl;
+	  std::ofstream of("regions4.g2");
+	  std::ofstream ofm("mid_regions4.g2");
+	  std::ofstream ofs("small_regions4.g2");
+	  writeRegionStage(of, ofm, ofs);
+	  if (surfaces_.size() > 0)
+	    {
+	      std::ofstream of("regsurf4.g2");
+	      writeRegionWithSurf(of);
+	    }
+	  std::ofstream of0("regions4_helix.g2");
+	  for (int ki=0; ki<(int)regions_.size(); ++ki)
+	    {
+	      if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+		{
+		  regions_[ki]->writeRegionInfo(of0);
+		  if (regions_[ki]->hasSurface())
+		    regions_[ki]->writeSurface(of0);
+		}
+	    }
+	}
+#endif
+    }
+#ifdef DEBUG_CHECK
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      int num = regions_[ki]->numPoints();
+      for (int ka=0; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+	  std::cout << "Inconsistent region pointers, post grow: " << ki << " " << ka << std::endl;
+    }
+
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, regions 4. " << ki << " " << tmpset.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+#endif
+  
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      if (regions_[ki]->hasSurface())
+	{
+	  vector<RevEngRegion*> grown_regions;
+	  vector<HedgeSurface*> adj_surfs;
+	  vector<RevEngEdge*> adj_edgs;
+	  regions_[ki]->mergeAdjacentSimilar(approx_tol_, angtol,
+					     grown_regions, adj_surfs, adj_edgs);
+	if (grown_regions.size() > 0 || adj_surfs.size() > 0)
+	  updateRegionsAndSurfaces(ki, grown_regions, adj_surfs);
+	for (size_t kr=0; kr<adj_edgs.size(); ++kr)
+	  {
+	    size_t kj;
+	    for (kj=0; kj<edges_.size(); ++kj)
+	      if (edges_[kj].get() == adj_edgs[kr])
+		break;
+	    if (kj < edges_.size())
+	      edges_.erase(edges_.begin()+kj);
+	  }
+	}
+    }
+  
+#ifdef DEBUG_CHECK
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      int num = regions_[ki]->numPoints();
+      for (int ka=0; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+	  std::cout << "Inconsistent region pointers, post mergeAdjacentSimilar: " << ki << " " << ka << std::endl;
+    }
+
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, regions 4_2. " << ki << " " << tmpset.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+#endif
+  
+#ifdef DEBUG
+      std::cout << "Post merge similar. Number of regions: " << regions_.size() << std::endl;
+
+      checkConsistence("Regions4_2");
+
+      if (regions_.size() > 0)
+	{
+	  std::cout << "Regions 4_2" << std::endl;
+	  std::ofstream of("regions4_2.g2");
+	  std::ofstream ofm("mid_regions4_2.g2");
+	  std::ofstream ofs("small_regions4_2.g2");
+	  writeRegionStage(of, ofm, ofs);
+	  std::ofstream of0("regions4_2_helix.g2");
+	  for (int ki=0; ki<(int)regions_.size(); ++ki)
+	    {
+	      if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+		{
+		  regions_[ki]->writeRegionInfo(of0);
+		  if (regions_[ki]->hasSurface())
+		    regions_[ki]->writeSurface(of0);
+		}
+	    }
+	}
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf4_2.g2");
+	  writeRegionWithSurf(of);
+	}
+#endif
+      
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+#ifdef DEBUG
+      std::ofstream ofp("init_growplane.g2");
+      regions_[ki]->writeRegionInfo(ofp);
+#endif
+      if (regions_[ki]->numPoints() < min_point_region_)
+	continue;   // Not a stable source
+	  
+      if (!regions_[ki]->hasSurface())
+	continue;
+
+      vector<RevEngRegion*> grown_regions;
+      vector<HedgeSurface*> adj_surfs;
+      vector<vector<RevEngPoint*> > added_groups;
+      regions_[ki]->growPlaneOrCyl(mainaxis_, min_point_region_, approx_tol_,
+				   angtol, grown_regions, adj_surfs,
+				   added_groups);
+      updateRegionsAndSurfaces(ki, grown_regions, adj_surfs);
+      vector<HedgeSurface*> dummy_surfs;
+      if (added_groups.size() > 0)
+	surfaceExtractOutput((int)ki, added_groups, dummy_surfs);
+      int stop_grow = 1;
+}
+
+#ifdef DEBUG
+      std::cout << "Grow plane. Number of regions: " << regions_.size() << std::endl;
+
+      checkConsistence("Regions4_3");
+
+      if (regions_.size() > 0)
+	{
+	  std::cout << "Regions 4_3" << std::endl;
+	  std::ofstream of("regions4_3.g2");
+	  std::ofstream ofm("mid_regions4_3.g2");
+	  std::ofstream ofs("small_regions4_3.g2");
+	  writeRegionStage(of, ofm, ofs);
+	  std::ofstream of0("regions4_3_helix.g2");
+	  for (int ki=0; ki<(int)regions_.size(); ++ki)
+	    {
+	      if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+		{
+		  regions_[ki]->writeRegionInfo(of0);
+		  if (regions_[ki]->hasSurface())
+		    regions_[ki]->writeSurface(of0);
+		}
+	    }
+	}
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf4_3.g2");
+	  writeRegionWithSurf(of);
+	}
+#endif
+      
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  // Simplify regions structure
+#ifdef DEBUG
+  std::cout << "Number of regions pre integrate: " << regions_.size() << std::endl;
+#endif
+  int num_reg = (int)regions_.size();
+  for (int ka=num_reg-1; ka>=0; --ka)
+    {
+      HedgeSurface* hedge = (regions_[ka]->hasSurface()) ?
+	regions_[ka]->getSurface(0) : 0;
+      bool to_be_removed = regions_[ka]->integrateInAdjacent(mean_edge_len_,
+							     min_next_, max_next_,
+							     approx_tol_, 0.5,
+							     max_nmb_outlier_);
+      if (to_be_removed)
+	{
+	  regions_[ka]->removeFromAdjacent();
+	  regions_[ka]->clearRegionAdjacency();
+	  if (hedge)
+	    {
+	      size_t kr;
+	      for (kr=0; kr<surfaces_.size(); ++kr)
+		if (surfaces_[kr].get() == hedge)
+		  break;
+	      if (kr < surfaces_.size())
+		surfaces_.erase(surfaces_.begin()+kr);
+	    }
+
+	  if (ka < num_reg-1)
+	    std::swap(regions_[ka], regions_[num_reg-1]);
+	  num_reg--;
+	}
+    }
+  if (num_reg < (int)regions_.size())
+    regions_.erase(regions_.begin()+num_reg, regions_.end());
+#ifdef DEBUG
+  std::cout << "Number of regions post integrate: " << regions_.size() << std::endl;
+  
+  checkConsistence("Regions5");
+
+  if (regions_.size() > 0)
+    {
+      std::cout << "Regions 5" << std::endl;
+      std::ofstream of("regions5.g2");
+      std::ofstream ofm("mid_regions5.g2");
+      std::ofstream ofs("small_regions5.g2");
+      writeRegionStage(of, ofm, ofs);
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf5.g2");
+	  writeRegionWithSurf(of);
+	}
+     }
+#endif
+  
+#ifdef DEBUG
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, regions 5. " << ki << " " << tmpset.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+#endif
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+  
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  
+  int stop_break2 = 1;
+}
+
+
+//===========================================================================
+void RevEng::updateAxesAndSurfaces()
+//===========================================================================
+{
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+  
+  // // First extract low accuracy points from groups with surface
+  double angtol = 5.0*anglim_;
+  // for (size_t ki=0; ki<regions_.size(); ++ki)
+  //   {
+  //     if (!regions_[ki]->hasSurface())
+  // 	continue;
+
+  //     vector<vector<RevEngPoint*> > added_groups;
+  //     vector<HedgeSurface*> dummy_surfs;
+  //     regions_[ki]->removeLowAccuracyPoints(min_point_region_, 
+  // 					    approx_tol_, angtol, added_groups);
+  //     if (added_groups.size() > 0)
+  // 	surfaceExtractOutput((int)ki, added_groups, dummy_surfs);
+  //   }
+
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+  
+  vector<int> reg_size(surfaces_.size());
+  for (size_t ki=0; ki<surfaces_.size(); ++ki)
+    reg_size[ki] = surfaces_[ki]->numPoints();
+
+  std::sort(reg_size.begin(), reg_size.end());
+
+  Point axis[3];
+  int min_num = reg_size[(int)reg_size.size()/4];
+  min_num = std::min(min_num, reg_size[reg_size.size()-1]/10);
+  min_num = std::max(min_num, reg_size[reg_size.size()-1]/100);
+  double max_ang = 0.1*M_PI;
+
+  Point plane_axis[3];
+  int num_pts1[3];
+  computeAxisFromPlane(mainaxis_, min_num, max_ang, plane_axis, num_pts1);
+
+  Point cyl_axis[3];
+  int num_pts2[3];
+  computeAxisFromCylinder(plane_axis, min_num, max_ang, cyl_axis, num_pts2);
+
+  // Update main axes. Prioritize information from planes
+  for (int ka=0; ka<3; ++ka)
+    {
+      num_pts1[ka] *= 2;
+      int all_pts = num_pts1[ka] + num_pts2[ka];
+      if (all_pts == 0)
+	continue;
+      double fac1 = (double)num_pts1[ka]/(double)(all_pts);
+      double fac2 = (double)num_pts2[ka]/(double)(all_pts);
+      if (cyl_axis[ka]*plane_axis[ka] < 0.0)
+	cyl_axis[ka] *= -1.0;
+      mainaxis_[ka] = fac1*plane_axis[ka] + fac2*cyl_axis[ka];
+      mainaxis_[ka].normalize();
+    }
+
+  // Ensure orthogonality
+  for (int ka=0; ka<3; ++ka)
+    for (int kb=ka+1; kb<3; ++kb)
+      if (num_pts1[ka] + num_pts2[ka] < num_pts1[kb] + num_pts2[kb])
+	{
+	  std::swap(num_pts1[ka], num_pts1[kb]);
+	  std::swap(num_pts2[ka], num_pts2[kb]);
+	  std::swap(mainaxis_[ka], mainaxis_[kb]);
+	}
+
+  Point tmp_axis[3];
+  tmp_axis[2] = mainaxis_[0].cross(mainaxis_[1]);
+  tmp_axis[1] = (num_pts1[2]+num_pts2[2] > 0) ?
+	    mainaxis_[2].cross(mainaxis_[0]) : mainaxis_[1];
+  tmp_axis[0] = (num_pts1[1]+num_pts2[1] > 0 && num_pts1[2]+num_pts2[2] > 0) ?
+		 mainaxis_[1].cross(mainaxis_[2]) : mainaxis_[0];
+  for (int ka=0; ka<3; ++ka)
+    {
+      if (tmp_axis[ka]*mainaxis_[ka] < 0.0)
+	tmp_axis[ka] *= -1.0;
+      if (num_pts1[ka] + num_pts2[ka] > 0)
+	tmp_axis[ka] = 0.5*(tmp_axis[ka] + mainaxis_[ka]);
+      tmp_axis[ka].normalize();
+    }
+
+  if (num_pts1[0]+num_pts2[0] > 0 && num_pts1[1]+num_pts2[1] > 0 &&
+      num_pts1[2]+num_pts2[2] > 0)
+    {
+      mainaxis_[0] = tmp_axis[1].cross(tmp_axis[2]);
+      mainaxis_[1] = tmp_axis[2].cross(tmp_axis[0]);
+      mainaxis_[2] = tmp_axis[0].cross(tmp_axis[1]);
+      for (int ka=0; ka<3; ++ka)
+	{
+	  if (mainaxis_[ka]*tmp_axis[ka] < 0.0)
+	    mainaxis_[ka] *= -1.0;
+	  mainaxis_[ka] = 0.5*(tmp_axis[ka] + mainaxis_[ka]);
+	  mainaxis_[ka].normalize();
+	}
+    }
+  else
+    {
+      for (int ka=0; ka<3; ++ka)
+	{
+	  mainaxis_[ka] = tmp_axis[ka];
+	}
+    }
+  // Point tmp_axis = mainaxis_[0].cross(mainaxis_[1]);
+  // tmp_axis.normalize();
+  // if (mainaxis_[2]*tmp_axis < 0.0)
+  //   tmp_axis *= -1;
+  // mainaxis_[2] = 0.5*(mainaxis_[2] + tmp_axis);
+  // tmp_axis = mainaxis_[2].cross(mainaxis_[0]);
+  // tmp_axis.normalize();
+  // if (mainaxis_[1]*tmp_axis < 0.0)
+  //   tmp_axis *= -1;
+  // mainaxis_[1] = 0.5*(mainaxis_[1] + tmp_axis);
+  // tmp_axis = mainaxis_[1].cross(mainaxis_[2]);
+  // tmp_axis.normalize();
+  // if (mainaxis_[0]*tmp_axis < 0.0)
+  //   tmp_axis *= -1;
+  // mainaxis_[0] = 0.5*(mainaxis_[0] + tmp_axis);
+
+  mainaxis_[2] = mainaxis_[0].cross(mainaxis_[1]);
+  mainaxis_[1] = mainaxis_[2].cross(mainaxis_[0]);
+  for (int ka=0; ka<3; ++ka)
+    mainaxis_[ka].normalize();
+
+  // Update surfaces
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      if (regions_[ki]->hasSurface())// && (!regions_[ki]->hasRevEdges()))
+	{
+	  /*bool updated =*/ (void)axisUpdate(ki, max_ang, angtol);
+	  // if (updated)
+	  //   {
+	  //     growSurface(ki);
+	  //     int stop_break0 = 1;
+	  //   }
+	}
+    }
+  
+#ifdef DEBUG
+  std::ofstream of0("regions5_2_0_helix.g2");
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+	{
+	  regions_[ki]->writeRegionInfo(of0);
+	  if (regions_[ki]->hasSurface())
+	    regions_[ki]->writeSurface(of0);
+	}
+    }
+#endif
+	  
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      if (regions_[ki]->hasSurface())
+	{
+	  vector<RevEngRegion*> grown_regions;
+	  vector<HedgeSurface*> adj_surfs;
+	  vector<RevEngEdge*> adj_edgs;
+	  regions_[ki]->mergeAdjacentSimilar(approx_tol_, angtol,
+					     grown_regions, adj_surfs, adj_edgs);
+	if (grown_regions.size() > 0 || adj_surfs.size() > 0)
+	  {
+	    updateRegionsAndSurfaces(ki, grown_regions, adj_surfs);
+	    for (size_t kr=0; kr<adj_edgs.size(); ++kr)
+	      {
+		size_t kj;
+		for (kj=0; kj<edges_.size(); ++kj)
+		  if (edges_[kj].get() == adj_edgs[kr])
+		    break;
+		if (kj < edges_.size())
+		  edges_.erase(edges_.begin()+kj);
+	      }
+	    // if (!regions_[ki]->hasRevEdges())
+	    //   {
+		bool updated = axisUpdate(ki, max_ang, angtol);
+		if (!updated)
+		  int stop_break1 = 1;
+	      // }
+	  }
+	}
+    }
+#ifdef DEBUG
+  std::cout << "Post merge similar. Number of regions: " << regions_.size() << std::endl;
+
+  checkConsistence("Regions5_2");
+
+  if (regions_.size() > 0)
+    {
+      std::cout << "Regions 5_2" << std::endl;
+      std::ofstream of("regions5_2.g2");
+      std::ofstream ofm("mid_regions5_2.g2");
+      std::ofstream ofs("small_regions5_2.g2");
+      writeRegionStage(of, ofm, ofs);
+      std::ofstream of0("regions5_2_helix.g2");
+      for (int ki=0; ki<(int)regions_.size(); ++ki)
+	{
+	  if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+	    {
+	      regions_[ki]->writeRegionInfo(of0);
+	      if (regions_[ki]->hasSurface())
+		regions_[ki]->writeSurface(of0);
+	    }
+	}
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf5_2.g2");
+	  writeRegionWithSurf(of);
+	}
+    }
+#endif
+#ifdef DEBUG
+  std::ofstream ofu1("unresolved.g2");
+  for (size_t kr=0; kr<regions_.size(); ++kr)
+    {
+      if (regions_[kr]->hasSurface())
+	continue;
+      if (regions_[kr]->hasAssociatedBlend())
+	continue;
+      regions_[kr]->writeRegionPoints(ofu1);
+    }
+#endif
+   int stop_break = 1;
+}
+
+
+//===========================================================================
+bool RevEng::axisUpdate(int ix, double max_ang, double angtol)
+//===========================================================================
+{
+  if (!regions_[ix]->hasSurface())
+    return false;
+
+  HedgeSurface *hsurf = regions_[ix]->getSurface(0);
+  shared_ptr<ElementarySurface> elem =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(hsurf->surface());
+  if (!elem.get())
+    return false;
+
+#ifdef DEBUG_AXIS
+  std::ofstream of("axis_adapt.g2");
+  elem->writeStandardHeader(of);
+  elem->write(of);
+  regions_[ix]->writeRegionPoints(of);
+#endif
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*>  > adj_elem, adj_elem_base;
+  regions_[ix]->getAdjacentElemInfo(adj_elem, adj_elem_base);
+  Point adj_axis, adj_pos;
+  double adj_ang = M_PI;
+  Point vec = elem->direction();
+  double pihalf = 0.5*M_PI;
+  double min_perpen = M_PI;
+  for (size_t ki=0; ki<adj_elem.size(); ++ki)
+    {
+      if (adj_elem[ki].first->instanceType() != Class_Plane &&
+	  adj_elem[ki].first->instanceType() != Class_Cylinder)
+	continue;
+      if (adj_elem[ki].second->hasBlendEdge())
+	continue; // Derived information
+      int sfflag = adj_elem[ki].second->getSurfaceFlag();
+      double anglim = (sfflag == ACCURACY_OK) ? 2.0*max_ang : max_ang;
+      Point dir = adj_elem[ki].first->direction();
+      double ang = vec.angle(dir);
+      if (fabs(pihalf - ang) < min_perpen)
+	min_perpen = fabs(pihalf - ang);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < anglim && ang < adj_ang)
+	{
+	  adj_ang = ang;
+	  adj_axis = dir;
+	  if (adj_elem[ki].first->instanceType() == Class_Cylinder)
+	    adj_pos = adj_elem[ki].first->location();
+	}
+    }
+
+  int ka_min = -1;
+  double ang_main = M_PI;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = vec.angle(mainaxis_[ka]);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < max_ang && ang < ang_main)
+	{
+	  ang_main = ang;
+	  ka_min = ka;
+	}
+    }
+  
+  bool updated = false;
+  if (adj_axis.dimension() != 0 || ka_min >= 0) 
+    updated = regions_[ix]->updateSurfaceWithAxis(min_point_region_, adj_axis, mainaxis_,
+						  ka_min, approx_tol_, angtol, adj_pos);
+  // if (updated == false && elem->instanceType() == Class_Plane && min_perpen < anglim_)
+  //   {
+  //     regions_[ix]->setPlaneParam(min_point_region_, mainaxis_, approx_tol_, angtol);
+  //   }
+  
+  return updated;
+}
+
+//===========================================================================
+void RevEng::recognizeEdges(bool only_curve)
+//===========================================================================
+{
+ // Ensure some limitation of surface size
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  double diag = low.dist(high);
+  double blendfac = 2.0;
+
+  // Ensure bounded surfaces
+  for (size_t ki=0; ki<surfaces_.size(); ++ki)
+    surfaces_[ki]->limitSurf(diag);
+
+  double angtol = 5.0*anglim_;
+  double pihalf = 0.5*M_PI;
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      if (!regions_[ki]->hasSurface())
+	continue;
+      if (regions_[ki]->hasAssociatedBlend())
+	continue;
+      if (regions_[ki]->hasBlendEdge())
+	continue;
+
+      vector<RevEngEdge*> rev_edgs1 = regions_[ki]->getAllRevEdges();
+      
+      int code;
+      int classtype = regions_[ki]->getSurface(0)->instanceType(code);
+      if (classtype != Class_Plane && classtype != Class_Cylinder &&
+	  classtype != Class_Cone && classtype != Class_Sphere)
+	continue;  // Preliminary
+
+      shared_ptr<ParamSurface> surf1 = regions_[ki]->getSurface(0)->surface();
+      shared_ptr<ElementarySurface> elem1 =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+      Point dir1 = elem1->direction();
+      if (elem1->instanceType() == Class_Plane &&
+	  dir1*regions_[ki]->getPoint(0)->getTriangNormal() < 0.0)
+	dir1 *= -1;
+      for (size_t kj=ki+1; kj<regions_.size(); ++kj)
+	{
+	  bool only_curve2 = only_curve;
+	  if (!regions_[kj]->hasSurface())
+	    continue;
+	  if (regions_[ki]->hasAssociatedBlend())
+	    continue;
+	  if (regions_[ki]->hasBlendEdge())
+	    continue;
+	  
+	  vector<RevEngEdge*> rev_edgs2 = regions_[kj]->getAllRevEdges();
+	  if (rev_edgs1.size() > 0 && rev_edgs2.size() > 0)
+	    {
+	      size_t kr, kh;
+	      for (kr=0; kr<rev_edgs1.size(); ++kr)
+		{
+		  for (kh=0; kh<rev_edgs2.size(); ++kh)
+		    if (rev_edgs1[kr] == rev_edgs2[kh])
+		      break;
+		  if (kh < rev_edgs2.size())
+		    break;
+		}
+	      if (kr < rev_edgs1.size())
+		continue;
+	    }
+	  
+ 	  int code2;
+	  int classtype2 = regions_[kj]->getSurface(0)->instanceType(code2);
+	  if (classtype2 != Class_Plane && classtype2 != Class_Cylinder &&
+	      classtype2 != Class_Cone && classtype2 != Class_Sphere)
+	    continue;  // Preliminary
+	  if (classtype == Class_Sphere || classtype2 == Class_Sphere)
+	    only_curve2 = true;
+	  if (classtype == Class_Cylinder && classtype2 == Class_Cylinder)
+	    continue;
+	  if (classtype == Class_Cone && classtype2 == Class_Cone)
+	    continue;
+	  if ((classtype == Class_Sphere && classtype2 != Class_Plane) ||
+	      (classtype2 == Class_Sphere && classtype != Class_Plane))
+	    continue;
+
+#ifdef DEBUG_EDGE
+	  std::ofstream of1("adj_regs.g2");
+	  regions_[ki]->writeRegionPoints(of1);
+	  regions_[ki]->writeSurface(of1);
+	  regions_[kj]->writeRegionPoints(of1);
+	  regions_[kj]->writeSurface(of1);
+#endif
+	  shared_ptr<ParamSurface> surf2 = regions_[kj]->getSurface(0)->surface();
+	  shared_ptr<ElementarySurface> elem2 =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+	  if (regions_[ki]->isAdjacent(regions_[kj].get())
+	      || (only_curve2 == false &&
+		  regions_[ki]->isNextToAdjacent(regions_[kj].get())))
+	    {
+	      Point dir2 = elem2->direction();
+	      if (elem2->instanceType() == Class_Plane &&
+		  dir2*regions_[kj]->getPoint(0)->getTriangNormal() < 0.0)
+		dir2 *= -1;
+	      double ang = dir1.angle(dir2);
+	      ang = std::min(ang, M_PI-ang);
+	      bool compute_edge = false;
+	      if (classtype == Class_Plane && classtype2 == Class_Plane)
+		{
+		  if (ang > blendfac*angtol)
+		    compute_edge = true;
+		}
+	      else if (classtype == Class_Plane || classtype2 == Class_Plane)
+		{
+		  if (ang < blendfac*angtol ||
+		      (only_curve2 && fabs(pihalf-ang) > blendfac*angtol))
+		    compute_edge = true;
+		  else if (fabs(pihalf-ang) < blendfac*angtol)
+		    {
+		      // Check for near tangential cases
+		      Point norm = (classtype == Class_Plane) ? elem1->direction() :
+			elem2->direction();
+		      double dlen = fabs((elem1->location() - elem2->location())*norm);
+		      double rad = (classtype == Class_Plane) ? elem2->radius(0.0, 0.0) :
+			elem1->radius(0.0, 0.0);   // Not appropriate for a cone
+		      if (dlen > rad + blendfac*approx_tol_ ||
+			  dlen < rad - blendfac*approx_tol_)
+			compute_edge = true;
+		    }
+		}
+	      else
+		{
+		  // One cone and one cylinder. Check for same axis and
+		  // a significant cone angle
+		  Point loc1 = elem1->location();
+		  Point loc2 = elem2->location();
+		  Point tmp = loc1 + ((loc2-loc1)*dir1)*dir1;
+		  if (ang >= blendfac*angtol || loc2.dist(tmp) > approx_tol_)
+		    compute_edge = false;
+		  else
+		    {
+		      double phi = 0.0;
+		      shared_ptr<Cone> cone1 =
+			dynamic_pointer_cast<Cone,ElementarySurface>(elem1);
+		      shared_ptr<Cone> cone2 =
+			dynamic_pointer_cast<Cone,ElementarySurface>(elem2);
+		      if (cone1.get())
+			phi = cone1->getConeAngle();
+		      else if (cone2.get())
+			phi = cone2->getConeAngle();
+		      double conefac = 4.0;
+		      if (fabs(phi) > conefac*angtol)
+			compute_edge = true;
+		      else
+			compute_edge = false;
+		    }
+		}
+	      
+
+	      if (compute_edge)
+		{
+		  // Make sure that cone domains do not cover the apex
+		  int ka;
+		  shared_ptr<ElementarySurface> elem;
+		  shared_ptr<RevEngRegion> reg;
+		  for (ka=0, elem=elem1, reg=regions_[ki]; ka<2;
+		       ++ka, elem=elem2, reg=regions_[kj])
+		    {
+		      if (elem->instanceType() == Class_Cone)
+			{
+			  shared_ptr<Cone> cone =
+			    dynamic_pointer_cast<Cone,ElementarySurface>(elem);
+			  double apar;
+			  int adir;
+			  cone->getDegenerateParam(apar, adir);
+			  if (adir > 0)
+			    {
+			      RectDomain dom = cone->getParameterBounds();
+			      double dom2[4];
+			      dom2[0] = dom.umin();
+			      dom2[1] = dom.umax();
+			      dom2[2] = dom.vmin();
+			      dom2[3] = dom.vmax();
+			      double dom3[4];
+			      reg->getDomain(dom3);
+			      adir--;
+			      // Assumes that the relevant part of the cone
+			      // does not cover the apex
+			      double midp = 0.5*(dom3[2*adir]+dom3[2*adir+1]);
+			      double del = 0.01*(dom3[2*adir+1]-dom3[2*adir]);
+			      if (midp < apar)
+				dom2[2*adir+1] = std::min(dom2[2*adir+1], apar-del);
+			      else
+				dom2[2*adir] = std::max(dom2[2*adir], apar+del);
+			      cone->setParameterBounds(dom2[0], dom2[2],
+							dom2[1], dom2[3]);
+			    }
+			}
+		    }
+
+		  vector<shared_ptr<RevEngEdge> > edges =
+		    defineEdgesBetween(ki, elem1, dir1,kj, elem2, dir2,
+				       only_curve2);
+		  if (edges.size() > 0)
+		    edges_.insert(edges_.end(), edges.begin(),edges.end());
+		}
+	    }
+	}
+    }
+}
+
+//===========================================================================
+void RevEng::firstEdges()
+//===========================================================================
+{
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  recognizeEdges();
+
+#ifdef DEBUG
+  checkConsistence("Regions6");
+
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions6" << std::endl;
+      std::ofstream of("regions6.g2");
+      std::ofstream ofm("mid_regions6.g2");
+      std::ofstream ofs("small_regions6.g2");
+      writeRegionStage(of, ofm, ofs);
+     }
+
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges6.g2");
+       writeEdgeStage(ofe);
+     }
+  
+#endif
+
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+   
+#ifdef DEBUG
+   std::cout << "Extend blend region collection" << std::endl;
+#endif
+   for (size_t ki=0; ki<edges_.size(); ++ki)
+     {
+       extendBlendAssociation(ki);
+     }
+   
+ #ifdef DEBUG
+  checkConsistence("Regions6_1");
+#endif
+  
+  // Just in case composed regions have been segmented
+  int min_num_point = min_point_region_/10;
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      if (regions_[ki]->hasSurface())
+	continue;
+      if (regions_[ki]->hasAssociatedBlend())
+	continue;
+      if (regions_[ki]->numPoints() < min_num_point)
+	continue;
+#ifdef DEBUG_EDGE
+      std::ofstream of("planar_merge_cand.g2");
+      regions_[ki]->writeRegionInfo(of);
+#endif
+      if (regions_[ki]->feasiblePlane(zero_H_, zero_K_))
+	{
+	  vector<RevEngRegion*> grown_regions;
+	  vector<HedgeSurface*> adj_surfs;
+	  bool merged = regions_[ki]->mergePlanarReg(zero_H_, zero_K_, 
+						     approx_tol_, mainaxis_,
+						     grown_regions);
+	  if (merged)
+	    {
+	      if (grown_regions.size() > 0 || adj_surfs.size() > 0)
+		updateRegionsAndSurfaces(ki, grown_regions, adj_surfs);
+	      regions_[ki]->updateRegionAdjacency();
+	    }
+	}
+    }
+
+  
+ #ifdef DEBUG
+  checkConsistence("Regions6_2");
+
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions6_2" << std::endl;
+      std::ofstream of("regions6_2.g2");
+      std::ofstream ofm("mid_regions6_2.g2");
+      std::ofstream ofs("small_regions6_2.g2");
+      writeRegionStage(of, ofm, ofs);
+     }
+
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges6_2.g2");
+       writeEdgeStage(ofe);
+     }
+  
+#endif
+   int stop_break = 1;
+}
+
+//===========================================================================
+void RevEng::extendBlendAssociation(size_t ix)
+//===========================================================================
+{
+  // Regions in blend area
+  vector<RevEngRegion*> blend_regs;
+  edges_[ix]->getAllBlendRegs(blend_regs);
+  size_t num_blend_regs = blend_regs.size();
+
+#ifdef DEBUG_BLEND
+  std::ofstream of1("blend_regs.g2");
+  for (size_t kj=0; kj<blend_regs.size(); ++kj)
+    blend_regs[kj]->writeRegionPoints(of1);
+#endif
+  // Intersection curve
+  vector<shared_ptr<CurveOnSurface> > cvs;
+  edges_[ix]->getCurve(cvs);
+#ifdef DEBUG_BLEND
+  for (size_t kj=0; kj<cvs.size(); ++kj)
+    {
+      cvs[kj]->spaceCurve()->writeStandardHeader(of1);
+      cvs[kj]->spaceCurve()->write(of1);
+    }
+  RevEngRegion *adj1, *adj2;
+  edges_[ix]->getAdjacent(adj1, adj2);
+  adj1->writeRegionPoints(of1);
+  adj2->writeRegionPoints(of1);
+#endif
+  
+  // Width
+  double width = edges_[ix]->getDistance();
+
+  for (size_t ki=0; ki<blend_regs.size(); ++ki)
+    {
+      vector<RevEngRegion*> new_blends;
+      blend_regs[ki]->neighbourBlends(cvs, 2.0*width, approx_tol_, new_blends);
+#ifdef DEBUG_BLEND
+      std::ofstream of2("blend_regs2.g2");
+      for (size_t kj=0; kj<new_blends.size(); ++kj)
+	new_blends[kj]->writeRegionPoints(of2);
+#endif
+      for (size_t kj=0; kj<new_blends.size(); ++kj)
+	{
+	  size_t kr;
+	  for (kr=0; kr<blend_regs.size(); ++kr)
+	    if (blend_regs[kr] == new_blends[kj])
+	      break;
+	  if (kr == blend_regs.size())
+	    blend_regs.push_back(new_blends[kj]);
+	}
+      int stop_break = 1;
+    }
+
+  for (size_t kj=num_blend_regs; kj<blend_regs.size(); ++kj)
+    {
+      edges_[ix]->addBlendRegion(blend_regs[kj]);
+      blend_regs[kj]->setAssociatedBlend(edges_[ix].get());
+    }
+  int stop_break2 = 1;
+}
+
+//===========================================================================
+bool RevEng::setBlendEdge(size_t ix)
+//===========================================================================
+{
+ Point low = bbox_.low();
+  Point high = bbox_.high();
+  double diag = low.dist(high);
+  double blendfac = 2.0;
+
+  //double pihalf = 0.5*M_PI;
+  double angtol = 5.0*anglim_;
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  regions_[ix]->getAdjacentElemInfo(adj_elem, adj_elem_base);
+  for (size_t ki=0; ki<adj_elem.size(); ++ki)
+    {
+      ClassType type1 = adj_elem[ki].first->instanceType();
+      if (type1 != Class_Plane && type1 != Class_Cylinder)
+	continue;
+      Point dir1 = adj_elem[ki].first->direction();
+      if (type1 == Class_Plane &&
+	  dir1*adj_elem[ki].second->getMeanNormalTriang() < 0.0)
+	dir1 *= -1;
+      for (size_t kj=ki+1; kj<adj_elem.size(); ++kj)
+	{
+	  ClassType type2 = adj_elem[kj].first->instanceType();
+	  if (type2 != Class_Plane && type2 != Class_Cylinder)
+	    continue;
+	  if (type1 == Class_Cylinder && type2 == Class_Cylinder)
+	    continue;
+	  Point dir2 = adj_elem[kj].first->direction();
+	  if (type2 == Class_Plane &&
+	      dir2*adj_elem[kj].second->getMeanNormalTriang() < 0.0)
+	    dir2 *= -1;
+	  double ang = dir1.angle(dir2);
+	  ang = std::min(ang, M_PI-ang);
+	  bool compute_edge = false;
+#ifdef DEBUG_BLEND
+	  std::ofstream of("adj_reg.g2");
+	  adj_elem[ki].second->writeRegionPoints(of);
+	  adj_elem[ki].second->writeSurface(of);
+	  adj_elem[kj].second->writeRegionPoints(of);
+	  adj_elem[kj].second->writeSurface(of);
+#endif
+	  if (type1 == Class_Plane && type2 == Class_Plane)
+	    {
+	      if (ang > blendfac*angtol)
+		compute_edge = true;
+	    }
+	  else
+	    {
+	      if (ang < blendfac*angtol)
+		compute_edge = true;
+	    }
+
+	  if (compute_edge)
+	    {
+	      // Check if the two regions already has a common edge
+	      vector<RevEngEdge*> edges1 = adj_elem[ki].second->getAllRevEdges();
+	      vector<RevEngEdge*> edges2 = adj_elem[kj].second->getAllRevEdges();
+	      size_t kr, kh;
+	      for (kr=0; kr<edges1.size(); ++kr)
+		{
+		  for (kh=0; kh<edges2.size(); ++kh)
+		    if (edges1[kr] == edges2[kh])
+		      break;
+		  if (kh < edges2.size())
+		    break;
+		}
+
+	      if (kr < edges1.size())
+		{
+		  // An edge exist already. Extend
+		  continue;  // For the time being
+		}
+	      else
+		{
+		  // Define new edge
+		  size_t ix1, ix2;
+		  for (ix1=0; ix1<regions_.size(); ++ix1)
+		    if (regions_[ix1].get() == adj_elem[ki].second)
+		      break;
+		  for (ix2=0; ix2<regions_.size(); ++ix2)
+		    if (regions_[ix2].get() == adj_elem[kj].second)
+		      break;
+		  if (ix1 == regions_.size() || ix2 == regions_.size())
+		    continue;
+
+		  // Make sure that the adjacent surfaces is bounded
+		  if (!adj_elem[ki].first->isBounded())
+		    regions_[ix1]->getSurface(0)->limitSurf(diag);
+		  if (!adj_elem[kj].first->isBounded())
+		    regions_[ix2]->getSurface(0)->limitSurf(diag);
+		  vector<shared_ptr<RevEngEdge> > edges =
+		    defineEdgesBetween(ix1, adj_elem[ki].first,
+				       dir1, ix2, adj_elem[kj].first, dir2);
+		  if (edges.size() > 0)
+		    {
+		      edges_.insert(edges_.end(), edges.begin(), edges.end());
+		      return true;
+		    }
+		}
+	    }
+	}
+    }
+  return false;
+}
+
+//===========================================================================
+vector<shared_ptr<RevEngEdge> >
+RevEng::defineEdgesBetween(size_t ix1, shared_ptr<ElementarySurface>& surf1,
+			   Point& dir1, size_t ix2,
+			   shared_ptr<ElementarySurface>& surf2, Point& dir2,
+			   bool only_curve, double lenlim0, bool check_common)
+//===========================================================================
+{
+  vector<shared_ptr<RevEngEdge> > edges;
+  if (regions_[ix1]->hasAssociatedBlend() || regions_[ix2]->hasAssociatedBlend())
+    return edges;
+  if (((regions_[ix1]->getSurfaceFlag() == ACCURACY_POOR  ||
+	regions_[ix1]->getSurfaceFlag() == NOT_SET) &&
+       (!regions_[ix1]->hasBlendEdge())) ||
+      ((regions_[ix2]->getSurfaceFlag() == ACCURACY_POOR  ||
+	regions_[ix2]->getSurfaceFlag() == NOT_SET) &&
+       (!regions_[ix2]->hasBlendEdge())))
+    return edges;
+
+  //double angtol = 5.0*anglim_;
+  double diag = bbox_.low().dist(bbox_.high());
+  double blendlim = std::min(0.1*diag, 30.0*mean_edge_len_);
+  double maxwidth = std::max(blendlim, 0.1*diag);
+  double lenlim = 10.0*mean_edge_len_; //blendlim;
+  if (lenlim0 > 0.0)
+    lenlim= std::min(lenlim, lenlim0);
+  double int_tol = 1.0e-6;
+  //bool plane1 = (surf1->instanceType() == Class_Plane);
+  //bool plane2 = (surf2->instanceType() == Class_Plane);
+  bool adjacent = regions_[ix1]->isAdjacent(regions_[ix2].get());
+
+  shared_ptr<BoundedSurface> bd1, bd2;
+  vector<shared_ptr<CurveOnSurface> > int_cvs1, int_cvs2;
+  BoundedUtils::getSurfaceIntersections(surf1, surf2, int_tol_,
+					int_cvs1, bd1,
+					int_cvs2, bd2);
+  if (int_cvs1.size() == 0)
+    return edges;
+
+  bool keep_length = (int_cvs1.size() == 1 && (regions_[ix1]->hasBlendEdge() ||
+						regions_[ix2]->hasBlendEdge()));
+		  
+  // Limit intersection curves to relevant intervals
+  if (!keep_length)
+    {
+      for (size_t kr=0; kr<int_cvs1.size(); )
+	{
+	  vector<shared_ptr<CurveOnSurface> > tmp_int1, tmp_int2;
+	  tmp_int1.push_back(int_cvs1[kr]);
+	  tmp_int2.push_back(int_cvs2[kr]);
+	  vector<pair<double,double> > t1_t2, t3_t4;
+	  bool OK1 =
+	    regions_[ix1]->getCurveRestriction(tmp_int1, approx_tol_,
+					       anglim_, t1_t2);
+	  bool OK2 =
+	    regions_[ix2]->getCurveRestriction(tmp_int2, approx_tol_,
+					       anglim_, t3_t4);
+      
+	  double t1 = std::max(t1_t2[0].first, t3_t4[0].first);
+	  double t2 = std::min(t1_t2[0].second, t3_t4[0].second);
+	  if (t2 > t1 && (t1 > int_cvs1[kr]->startparam() ||
+			  t2 < int_cvs1[kr]->endparam()))
+	    {
+	      double pmin = std::max(t1, int_cvs1[kr]->startparam());
+	      double pmax = std::min(t2, int_cvs1[kr]->endparam());
+	      shared_ptr<CurveOnSurface> sub1(int_cvs1[kr]->subCurve(pmin,pmax));
+	      int_cvs1[kr] = sub1;
+	      shared_ptr<CurveOnSurface> sub2(int_cvs2[kr]->subCurve(pmin,pmax));
+	      int_cvs2[kr] = sub2;
+	    }
+
+	  if (t2 <= t1 || int_cvs1[kr]->estimatedCurveLength() < lenlim)
+	    {
+	      int_cvs1.erase(int_cvs1.begin()+kr);
+	      int_cvs2.erase(int_cvs2.begin()+kr);
+	    }
+	  else
+	    ++kr;
+	}
+      if (int_cvs1.size() == 0)
+	return edges;
+    }
+  
+  vector<RevEngRegion*> common_reg =
+    regions_[ix1]->commonAdjacent(regions_[ix2].get());
+  for (size_t kj=0; kj<common_reg.size(); )
+    {
+      if (common_reg[kj]->hasAssociatedBlend() || common_reg[kj]->hasBlendEdge())
+  	common_reg.erase(common_reg.begin()+kj);
+      else
+  	++kj;
+    }
+
+  if (!adjacent && check_common)
+    {
+      // Check if any of the regions between is more significant
+      int num1 = regions_[ix1]->numPoints();
+      int num2 = regions_[ix2]->numPoints();
+      int fac = 2;
+      for (size_t kj=0; kj<common_reg.size(); ++kj)
+	{
+	  if (!common_reg[kj]->hasSurface())
+	    continue;
+	  int num3 = common_reg[kj]->numPoints();
+	  if (num3 > fac*std::min(num1, num2))
+	    return edges;  // Could be a small passage. 
+	}
+    }
+		  
+#ifdef DEBUG_EDGE
+  std::ofstream of1("adj_regs_cv.g2");
+  for (size_t kr=0; kr<int_cvs1.size(); ++kr)
+    {
+      shared_ptr<ParamCurve> cv = int_cvs1[kr]->spaceCurve();
+      cv->writeStandardHeader(of1);
+      cv->write(of1);
+    }
+
+  if (common_reg.size() > 0)
+    {
+      std::ofstream of2("between_regs.g2");
+      for (size_t kr=0; kr<common_reg.size(); ++kr)
+	{
+	  common_reg[kr]->writeRegionPoints(of2);
+	  if (common_reg[kr]->hasSurface())
+	    common_reg[kr]->writeSurface(of2);
+	}
+    }
+#endif
+
+  vector<RevEngRegion*> regs1;
+  regs1.push_back(regions_[ix2].get());
+  regs1.insert(regs1.end(), common_reg.begin(), common_reg.end());
+  vector<RevEngPoint*> bd_pts1 =
+    regions_[ix1]->extractBdPoints(); //regs1);
+
+  vector<RevEngRegion*> regs2;
+  regs2.push_back(regions_[ix1].get());
+  regs2.insert(regs2.end(), common_reg.begin(), common_reg.end());
+  vector<RevEngPoint*> bd_pts2 =
+    regions_[ix2]->extractBdPoints();//regs2);
+  if (bd_pts1.size() == 0 || bd_pts2.size() == 0)
+    return edges;
+  
+#ifdef DEBUG_EDGE
+  std::ofstream of3("bd_pts.g2");
+  of3 << "400 1 0 4 255 0 0 255" << std::endl;
+  of3 << bd_pts1.size() << std::endl;
+  for (size_t kr=0; kr<bd_pts1.size(); ++kr)
+    of3 << bd_pts1[kr]->getPoint() << std::endl;
+  of3 << "400 1 0 4 255 0 0 255" << std::endl;
+  of3 << bd_pts2.size() << std::endl;
+  for (size_t kr=0; kr<bd_pts2.size(); ++kr)
+    of3 << bd_pts2[kr]->getPoint() << std::endl;
+#endif
+
+  // if (int_cvs1.size() > 1)
+  //   {
+  //     // Ensure consistent curve parameterization. Sort
+  //     vector<Point> startpt(int_cvs1.size()), endpt(int_cvs1.size());
+  //     for (size_t kr=0; kr<int_cvs1.size(); ++kr)
+  // 	{
+  // 	  startpt[kr] = int_cvs1[kr]->ParamCurve::point(int_cvs1[kr]->startparam());
+  // 	  endpt[kr] = int_cvs1[kr]->ParamCurve::point(int_cvs1[kr]->endparam());
+  // 	}
+  //     int stop_breakp = 1;
+  //   }
+  int num_in_lim1=0, num_in_lim2=0;
+  vector<double> width2;
+  vector<shared_ptr<CurveOnSurface> > cvs1, cvs2;
+  for (size_t kr=0; kr<int_cvs1.size(); ++kr)
+    {
+      vector<pair<double,double> > tmin_tmax;
+      vector<double> width;
+      vector<shared_ptr<CurveOnSurface> > tmp_int1, tmp_int2;
+      tmp_int1.push_back(int_cvs1[kr]);
+      tmp_int2.push_back(int_cvs2[kr]);
+      vector<pair<double, double> > t5_t6, t7_t8;
+      vector<double> wwd1, wwd2;
+      int numlim1, numlim2;
+      regions_[ix1]->estimateBlendDimensions(tmp_int1, bd_pts1,
+					     approx_tol_, blendlim,
+					     t5_t6, wwd1, numlim1);
+
+      regions_[ix2]->estimateBlendDimensions(tmp_int2, bd_pts2,
+					     approx_tol_, blendlim,
+					     t7_t8, wwd2, numlim2);
+      if (t5_t6.size() == 0 || t7_t8.size() == 0)
+	continue;
+      num_in_lim1 += numlim1;
+      num_in_lim2 += numlim2;
+
+     // Unify intersection curve limitations
+      if (keep_length)
+	{
+	  tmin_tmax.push_back(std::make_pair(int_cvs1[kr]->startparam(),
+					     int_cvs1[kr]->endparam()));
+	  double wwd = 0.0;
+	  double w1min = std::numeric_limits<double>::max();
+	  double w2min = std::numeric_limits<double>::max();
+	  for (size_t kh=0; kh<wwd1.size(); ++kh)
+	    {
+	      wwd += wwd1[kh];
+	      w1min = std::min(w1min, wwd1[kr]);
+	    }
+	  for (size_t kh=0; kh<wwd2.size(); ++kh)
+	    {
+	      wwd += wwd2[kh];
+	      w2min = std::min(w2min, wwd2[kh]);
+	    }
+	  wwd /= (double)(wwd1.size()+wwd2.size());
+	  wwd = std::max(wwd, std::max(w1min, w2min));
+	  width.push_back(wwd);
+	}
+      else
+	{
+	  size_t kk1, kk2;
+	  for (kk1=0, kk2=0; kk1<wwd1.size() && kk2<wwd2.size(); )
+	    {
+	      for (; kk1<wwd1.size() && t7_t8[kk2].first >= t5_t6[kk1].second; ++kk1);
+	      if (kk1 == wwd1.size())
+		break;
+	      for (; kk2<wwd2.size() && t5_t6[kk1].first >= t7_t8[kk2].second; ++kk2);
+	      tmin_tmax.push_back(std::make_pair(std::max(t5_t6[kk1].first,t7_t8[kk2].first),
+						 std::min(t5_t6[kk1].second,t7_t8[kk2].second)));
+	      width.push_back(0.5*(wwd1[kk1]+wwd2[kk2]));
+	      if (t5_t6[kk1].second < t7_t8[kk2].second)
+		++kk1;
+	      else if (t7_t8[kk2].second < t5_t6[kk1].second)
+		++kk2;
+	      else
+		{
+		  ++kk1;
+		  ++kk2;
+		}
+	    }
+	}
+
+      size_t ki;
+      for (ki=0; ki<width.size(); ++ki)
+	{
+	  double tmin = tmin_tmax[ki].first;
+	  double tmax = tmin_tmax[ki].second;
+	  double width3 = width[ki];
+	  if (tmax <= tmin+int_tol || width3 > maxwidth)
+	    continue;
+      
+	  double tp1 = std::max(int_cvs1[kr]->startparam(), tmin);
+	  double tp2 = std::min(int_cvs1[kr]->endparam(), tmax);
+	  if (tp2 <= tp1+int_tol)
+	    continue;
+	  if (tp1 > int_cvs1[kr]->startparam()+int_tol ||
+	      tp2 < int_cvs1[kr]->endparam()-int_tol)
+	    {
+	      shared_ptr<CurveOnSurface> sub1(int_cvs1[kr]->subCurve(tp1, tp2));
+	      shared_ptr<CurveOnSurface> sub2(int_cvs2[kr]->subCurve(tp1, tp2));
+	      cvs1.push_back(sub1);
+	      cvs2.push_back(sub2);
+	    }
+	  else if (fabs(tp1-int_cvs1[kr]->startparam()) < int_tol &&
+		   fabs(tp2-int_cvs1[kr]->endparam()) < int_tol)
+	    {
+	      cvs1.push_back(int_cvs1[kr]);
+	      cvs2.push_back(int_cvs2[kr]);
+	    }
+	  width2.push_back(width3);
+	}
+    }
+   
+  if (width2.size() == 0)
+    return edges;
+  if (num_in_lim1 == 0 || num_in_lim2 == 0)
+    return edges;
+  if (cvs1.size() == 0)
+    return edges;
+
+#ifdef DEBUG_EDGE
+  std::ofstream of1e("one_edgcv.g2");
+  for (size_t kr=0; kr<cvs1.size(); ++kr)
+    {
+      cvs1[kr]->spaceCurve()->writeStandardHeader(of1e);
+      cvs1[kr]->spaceCurve()->write(of1e);
+    }
+#endif
+  for (size_t kj=0; kj<cvs1.size(); ++kj)
+    {
+      shared_ptr<RevEngEdge> edg = defineOneEdge(ix1, surf1, dir1, ix2,
+						 surf2, dir2, cvs1[kj],
+						 cvs2[kj], width2[kj],
+						 common_reg, only_curve,
+						 check_common);
+      if (edg.get())
+	edges.push_back(edg);
+    }
+
+  return edges;
+}
+	
+
+//===========================================================================
+shared_ptr<RevEngEdge> 
+RevEng::defineOneEdge(size_t ix1, shared_ptr<ElementarySurface>& surf1,
+		      Point& dir1, size_t ix2,
+		      shared_ptr<ElementarySurface>& surf2, Point& dir2,
+		      shared_ptr<CurveOnSurface>& int_cv1,
+		      shared_ptr<CurveOnSurface>& int_cv2,
+		      double width, vector<RevEngRegion*>& common_reg,
+		      bool only_curve, bool check_common)
+//===========================================================================
+{
+  shared_ptr<RevEngEdge> dummy_edg;
+  bool plane1 = (surf1->instanceType() == Class_Plane);
+  bool plane2 = (surf2->instanceType() == Class_Plane);
+  double angtol = 5.0*anglim_;
+  double tol10 = 10.0*approx_tol_;
+  int min_pt_blend = 20;
+
+  if (plane1)
+    {
+      // Make sure that the plane normal poins out
+      Point avnorm = regions_[ix1]->getMeanNormal();
+      if (dir1*avnorm < 0.0)
+	dir1 *= -1;
+    }
+  if (plane2)
+    {
+      // Make sure that the plane normal poins out
+      Point avnorm = regions_[ix2]->getMeanNormal();
+      if (dir2*avnorm < 0.0)
+	dir2 *= -1;
+    }
+  int state = (plane1 || plane2) ? 1 : 2;
+
+
+  vector<vector<RevEngPoint*> > near_pts(common_reg.size()+2);
+  vector<RevEngPoint*> curr_near1, curr_near2;
+  double tmin1 = int_cv1->startparam();
+  double tmax1 = int_cv1->endparam();
+  if (!only_curve)
+    regions_[ix1]->getNearPoints(int_cv1, tmin1, tmax1, width,
+				 angtol, curr_near1);
+  double tmin2 = int_cv2->startparam();
+  double tmax2 = int_cv2->endparam();
+  if (!only_curve)
+    regions_[ix2]->getNearPoints(int_cv2, tmin2, tmax2, width,
+				 angtol, curr_near2);
+  if (curr_near1.size() == 0 && curr_near2.size() == 0 && (!only_curve))
+    return dummy_edg;
+
+  RevEngPoint *distant1 = 0, *distant2 = 0;
+  vector<RevEngPoint*> reg1_pts, reg2_pts;
+  if (only_curve)
+    {
+      reg1_pts = regions_[ix1]->getPoints();
+      reg2_pts = regions_[ix2]->getPoints();
+    }
+  distant1 = getDistantPoint(int_cv1, std::max(tmin1, tmin2),
+			     std::min(tmax1, tmax2), approx_tol_,
+			     width, (curr_near1.size() > 0) ? curr_near1 :
+			     reg1_pts);
+  distant2 = getDistantPoint(int_cv2, std::max(tmin1, tmin2),
+			     std::min(tmax1, tmax2), approx_tol_,
+			     width, (curr_near2.size() > 0) ? curr_near2 :
+			     reg2_pts);
+  if (!distant1)
+    {
+      Point mid;
+      int_cv1->point(mid, 0.5*(int_cv1->startparam()+int_cv1->endparam()));
+      double distmid;
+      distant1 = regions_[ix1]->closestPoint(mid, distmid);
+    }
+  if (!distant2)
+    {
+      Point mid;
+      int_cv2->point(mid, 0.5*(int_cv2->startparam()+int_cv2->endparam()));
+      double distmid;
+      distant2 = regions_[ix2]->closestPoint(mid, distmid);
+    }
+  if ((!distant1) || (!distant2))
+    return dummy_edg;
+
+  vector<Point> der(2);
+  Vector3D xyz1 = distant1->getPoint();
+  Vector3D xyz2 = distant2->getPoint();
+  Point loc1(xyz1[0], xyz1[1], xyz1[2]);
+  Point loc2(xyz2[0], xyz2[1], xyz2[2]);
+  Point distpt = (plane1) ? loc1 : loc2;
+  double dist;
+  double tpar;
+  Point close;
+  int_cv1->closestPoint(distpt, int_cv1->startparam(),
+			int_cv1->endparam(), tpar, close, dist);
+  int_cv1->point(der, tpar, 1);
+		  
+  Point loc1_2 = loc1 - ((loc1-der[0])*der[1])*der[1];
+  Point loc2_2 = loc2 - ((loc2-der[0])*der[1])*der[1];
+  Point vec = loc1_2 - loc2_2;
+  bool outer1, outer2;
+  Point pos2;
+  if (plane1)
+    outer1 = (dir1*vec < 0.0);
+  else if (plane2)
+    {
+      // Cylinder or cone combined with plane
+      Point pos = surf1->location();
+       double vval = (der[0]-pos)*dir1;
+     double rad = surf1->radius(0.0,vval);
+      pos2 = pos + vval*dir1;
+      Point loc2_3 = loc2 - ((loc2-der[0])*dir1)*dir1;
+      outer1 = (pos2.dist(loc2_3) < rad);
+    }
+  else if (surf1->instanceType() == Class_Cylinder)
+    {
+      // Cylinder combined with cone
+      Point axs = surf1->direction();
+      Point tmp = loc1 + ((der[0]-loc1)*axs)*axs;
+      Point vec2 = loc1 - tmp;
+      Vector2D uv = distant2->getPar();
+      double rad1 = surf2->radius(0.0, uv[1]);
+      double rad2 = surf1->radius(0.0, 0.0);
+      outer1 = (rad1 >= rad2);
+      if (vec2*axs < 0.0)
+	outer1 = (!outer1);
+    }
+  else
+    {
+      // Cone combined with cylinder
+      Vector2D uv = distant1->getPar();
+      double rad1 = surf1->radius(0.0, uv[1]);
+      double rad2 = surf2->radius(0.0, 0.0);
+      outer1 = (rad1 >= rad2);
+    }
+  
+  if (plane2)
+    outer2 = (dir2*vec > 0.0);
+  else if (plane1)
+    {
+      Point pos = surf2->location();
+      double vval = (der[0]-pos)*dir2;
+      double rad = surf2->radius(0.0,vval);
+      pos2 = pos + vval*dir2;
+      Point loc1_3 = loc1 - ((loc1-der[0])*dir2)*dir2;
+      outer2 = (pos2.dist(loc1_3) < rad);
+    }
+  else if (surf2->instanceType() == Class_Cylinder)
+    {
+      // Cylinder combined with cone
+      Vector2D uv = distant1->getPar();
+      double rad1 = surf1->radius(0.0, uv[1]);
+      double rad2 = surf2->radius(0.0, 0.0);
+      Point axs = surf2->direction();
+      Point tmp = loc2 + ((der[0]-loc2)*axs)*axs;
+      Point vec2 = loc2 - tmp;
+      outer2 = (rad1 >= rad2);
+      if (vec2*axs < 0.0)
+	outer2 = (!outer2);
+    }
+  else
+    {
+      // Cone combined with cylinder
+      Vector2D uv = distant2->getPar();
+      double rad1 = surf2->radius(0.0, uv[1]);
+      double rad2 = surf1->radius(0.0, 0.0);
+      outer2 = (rad1 >= rad2);
+    }
+
+  double mindist1=0.0, mindist2=0.0;
+  near_pts[0] = (curr_near1.size() == 0 || state == 2) ? curr_near1 :
+    regions_[ix2]->removeOutOfSurf(curr_near1, tol10,
+				   angtol, outer2, mindist1);
+  near_pts[1] = (curr_near2.size() == 0 || state == 2) ? curr_near2 :
+    regions_[ix1]->removeOutOfSurf(curr_near2, tol10,
+				   angtol, outer1, mindist2);
+
+  if ((near_pts[0].size() == 0 || near_pts[1].size() == 0) && (!only_curve))
+    return dummy_edg;
+  int nmb_near = (int)(near_pts[0].size() + near_pts[1].size());
+  for (size_t kr=0; kr<common_reg.size(); ++kr)
+    {
+      vector<RevEngPoint*> adj_near1, adj_near2;
+      double dummy_min = 0.0;
+      double tmin3 = int_cv1->startparam();
+      double tmax3 = int_cv1->endparam();
+      common_reg[kr]->getNearPoints(int_cv1, tmin3, tmax3, width,
+				    angtol, adj_near1);
+      if (state == 1)
+	{
+	  adj_near2 =
+	    regions_[ix1]->removeOutOfSurf(adj_near1, tol10,
+					   angtol, outer1, dummy_min);
+	  near_pts[2+kr] = 
+	    regions_[ix2]->removeOutOfSurf(adj_near2, tol10,
+					   angtol, outer2, dummy_min);
+	}
+      else
+	near_pts[2+kr] = adj_near1;
+      
+      nmb_near += (int)near_pts[2+kr].size();
+    }
+  if (nmb_near < min_pt_blend && (!only_curve))
+    return dummy_edg;
+  
+#ifdef DEBUG_EDGE
+  std::ofstream of4("blend_pts.g2");
+  for (size_t kr=0; kr<near_pts.size(); ++kr)
+    {
+      of4 << "400 1 0 0" << std::endl;
+      of4 << near_pts[kr].size() << std::endl;
+      for (size_t kh=0; kh<near_pts[kr].size(); ++kh)
+	of4 << near_pts[kr][kh]->getPoint() << std::endl;
+    }
+#endif
+
+  // Ensure connection between adjacent regions
+  bool adjacent = only_curve ? true : false;
+  bool connection = false;
+  for (size_t kr=0; kr<near_pts[0].size(); ++kr)
+    {
+      for (size_t kh=0; kh<near_pts[1].size(); ++kh)
+	if (near_pts[0][kr]->isNeighbour(near_pts[1][kh]))
+	  {
+	    adjacent = true;
+	    break;
+	  }
+      if (adjacent)
+	break;
+    }
+  
+  for (size_t kr=2; kr<near_pts.size(); ++kr)
+    {
+      bool con1 = false, con2 = false;
+      for (size_t kh=0; kh<near_pts[kr].size(); ++kh)
+	{
+	  if (kh != 0 && near_pts[kr][kh]->nextToRegion(regions_[ix1].get()))
+	    con1 = true;
+	  if (kh != 1 && near_pts[kr][kh]->nextToRegion(regions_[ix2].get()))
+	    con2 = true;
+	  if (con1 && con2)
+	    {
+	      connection = true;
+	      break;
+	    }
+	}
+      if (connection)
+	break;
+    }
+  if ((!connection) && (!adjacent) && (!regions_[ix1]->hasBlendEdge()) &&
+      (!regions_[ix2]->hasBlendEdge()))
+    return dummy_edg;
+	      
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > blend_groups;
+  for (size_t kr=0; kr<near_pts.size(); ++kr)
+    blend_groups.push_back(std::make_pair(near_pts[kr].begin(),
+					  near_pts[kr].end()));
+  double radius = 0.0, ylen = 0.0;
+  if (!only_curve)
+    {
+      if (plane1 && plane2)
+	{
+	  Point lin1 = der[1].cross(dir1);
+	  Point lin2 = der[1].cross(dir2);
+	  lin1.normalize();
+	  if (lin1*dir2 < 0.0)
+	    lin1 *= -1;
+	  lin2.normalize();
+	  if (lin2*dir1 < 0.0)
+	    lin2 *= -1;
+	  radius = computeCylinderRadius(near_pts, width, der[0],
+					 der[1], lin1, lin2);
+	  Point vec = lin1 + lin2;
+	  vec.normalize();
+	  double alpha = lin1.angle(lin2);
+	  double fac = 1.0/sin(0.5*alpha);
+	  Point centre = der[0] - fac*radius*vec;
+	  //double ang = lin1.angle(lin2);
+	  double xlen = der[0].dist(centre);
+	  ylen = sqrt(xlen*xlen - radius*radius);
+	}
+      else
+	{
+	  int sgn = 1;
+	  if ((plane1 && surf1->direction()*dir1 < 0.0) ||
+	      (plane2 && surf2->direction()*dir2 < 0.0))
+	    sgn = -1;
+	  double d2 = 0.0;
+	  radius = computeTorusRadius(near_pts, int_cv1, surf1, surf2, 
+				      width, outer1, outer2, sgn, d2);
+
+
+	  Point centre, normal, Cx;
+	  double Rrad;
+	  bool OK = getTorusParameters(surf1, surf2, der[0], radius, d2, outer1, 
+				       outer2, sgn, Rrad, centre, normal, Cx,
+				       check_common);
+	  if (!OK)
+	    return dummy_edg;
+	  double xlen = der[0].dist(centre);
+	  ylen = fabs(xlen - Rrad);
+	}
+      ylen *= 1.5;  // Allow some slack
+      ylen= std::max(ylen, 1.1*std::max(mindist1, mindist2));
+    
+
+      // Reduce near points from adjacent groups according to the updated width
+      if (ylen < width)
+	{
+	  for (size_t kr=2; kr<near_pts.size(); ++kr)
+	    {
+	      vector<RevEngPoint*> near_pts2;
+	      common_reg[kr-2]->getNearPoints2(near_pts[kr], int_cv1, ylen, near_pts2);
+	      std::swap(near_pts[kr], near_pts2);
+	    }
+	}
+    }
+
+#ifdef DEBUG_EDGE
+  std::ofstream of5("blend_pts2.g2");
+  for (size_t kr=0; kr<near_pts.size(); ++kr)
+    {
+      of5 << "400 1 0 0" << std::endl;
+      of5 << near_pts[kr].size() << std::endl;
+      for (size_t kh=0; kh<near_pts[kr].size(); ++kh)
+	of5 << near_pts[kr][kh]->getPoint() << std::endl;
+    }
+#endif
+
+  // An intersection/blend curve is found
+  // Segment identified adjacent groups according to curve
+  vector<RevEngRegion*> adj_regs;
+  vector<size_t> remove_ix;
+  for (size_t kr=0; kr<common_reg.size(); ++kr)
+    {
+      if (near_pts[kr+2].size() == 0)
+	continue;
+
+      if ((int)near_pts[kr+2].size() < common_reg[kr]->numPoints())
+	{
+	  int num_init = common_reg[kr]->numPoints();
+	  vector<vector<RevEngPoint*> > out_groups;
+	  vector<HedgeSurface*> out_sfs;
+	  vector<vector<RevEngPoint*> > near_groups;
+	  common_reg[kr]->extractSpesPoints(near_pts[kr+2], near_groups);
+	  common_reg[kr]->updateInfo();
+	  common_reg[kr]->splitRegion(out_groups);
+	  if (common_reg[kr]->hasSurface() && common_reg[kr]->numPoints() < num_init/2 &&
+	      !(common_reg[kr]->hasRevEdges() || common_reg[kr]->hasTrimEdges()))
+	    {
+	      int num_sf = common_reg[kr]->numSurface();
+	      for (int ka=0; ka<num_sf; ++ka)
+		out_sfs.push_back(common_reg[kr]->getSurface(ka));
+	      common_reg[kr]->clearSurface();
+	    }
+
+	  // Make new region
+	  shared_ptr<RevEngRegion> curr_adj(new RevEngRegion(common_reg[kr]->getClassificationType(),
+							     common_reg[kr]->getEdgeClassificationType(),
+							     near_pts[kr+2]));
+	  regions_.push_back(curr_adj);
+	  adj_regs.push_back(curr_adj.get());
+
+	  size_t kh=0;
+	  for (kh=0; kh<regions_.size(); ++kh)
+	    if (common_reg[kr] == regions_[kh].get())
+	      break;
+	  surfaceExtractOutput((int)kh, out_groups, out_sfs);
+	}
+      else
+	{
+	  adj_regs.push_back(common_reg[kr]);
+	  remove_ix.push_back(kr);
+	}
+    }
+
+  for (int ka=(int)remove_ix.size()-1; ka>=0; --ka)
+    common_reg.erase(common_reg.begin() + remove_ix[ka]);
+
+  for (size_t kr=0; kr<adj_regs.size(); ++kr)
+    {
+      vector<vector<RevEngPoint*> > curr_out;
+      vector<HedgeSurface*> dummy_surfs;
+      adj_regs[kr]->splitRegion(curr_out);
+      if (curr_out.size() > 0)
+	{
+	  size_t kh=0;
+	  for (kh=0; kh<regions_.size(); ++kh)
+	    if (adj_regs[kr] == regions_[kh].get())
+	      break;
+	  surfaceExtractOutput((int)kh, curr_out, dummy_surfs);
+	}
+    }
+
+  for (size_t kr=0; kr<adj_regs.size(); ++kr)
+    adj_regs[kr]->updateRegionAdjacency();
+  regions_[ix1]->updateRegionAdjacency();
+  regions_[ix2]->updateRegionAdjacency();
+  
+  int edge_type = (only_curve) ? NOT_BLEND : BLEND_NOT_SET;
+  vector<shared_ptr<CurveOnSurface> > int_cvs1(1, int_cv1);
+  vector<shared_ptr<CurveOnSurface> > int_cvs2(1, int_cv2);
+  shared_ptr<RevEngEdge> edge(new RevEngEdge(edge_type, regions_[ix1].get(),
+					     int_cvs1, outer1, regions_[ix2].get(),
+					     int_cvs2, outer2, radius,
+					     std::min(width, ylen)));
+  regions_[ix1]->addRevEdge(edge.get());
+  regions_[ix2]->addRevEdge(edge.get());
+  if (adj_regs.size() > 0)
+    edge->addBlendRegions(adj_regs);
+  for (size_t kr=0; kr<adj_regs.size(); ++kr)
+    adj_regs[kr]->setAssociatedBlend(edge.get());
+
+  return edge;
+}
+
+					  
+//===========================================================================
+RevEngPoint* RevEng::getDistantPoint(shared_ptr<CurveOnSurface>& cv,
+				     double tmin, double tmax, double dd,
+				     double width, vector<RevEngPoint*>& points)
+//===========================================================================
+{
+  RevEngPoint *distant = 0;
+  double ptdist = 0.0;
+  double eps = 1.0e-9;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      if (points[ki]->getSurfaceDist() > dd)
+	continue;
+      
+      double tpar, dist;
+      Point close;
+      Vector3D xyz = points[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      cv->closestPoint(pt, cv->startparam(), cv->endparam(),
+		       tpar, close, dist);
+      if (tpar > tmin+eps && tpar < tmax-eps && dist > ptdist &&
+	  dist < width)
+	{
+	  ptdist = dist;
+	  distant = points[ki];
+	}
+    }
+  return distant;
+}
+
+
+
+//===========================================================================
+void RevEng::computeAxisFromCylinder(Point initaxis[3], int min_num, double max_ang,
+				     Point axis[3], int num_points[3])
+//===========================================================================
+{
+  vector<vector<pair<std::vector<RevEngPoint*>::iterator,
+		     std::vector<RevEngPoint*>::iterator> > > points(3);
+  num_points[0] =  num_points[1] = num_points[2] = 0;
+  for (int ka=0; ka<3; ++ka)
+    axis[ka] = initaxis[ka];
+
+  Point axis0[3];
+  for (int ka=0; ka<3; ++ka)
+    axis0[ka] = initaxis[ka];
+  double max_ang2 = 0.5*M_PI - 1.0e-9;
+  for (int kc=0; kc<2; ++kc)
+    {
+      Point dummy(0.0, 0.0, 0.0);
+      vector<double> min_angle(3, max_ang2);
+      vector<Point> min_axis(3, dummy);
+   
+      for (size_t ki=0; ki<surfaces_.size(); ++ki)
+	{
+	  int sfcode;
+	  ClassType type = surfaces_[ki]->instanceType(sfcode);
+	  if ((type != Class_Cylinder) && (type != Class_Cone))
+	    continue;
+      
+	  shared_ptr<ElementarySurface> curr =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surfaces_[ki]->surface());
+	  if (!curr.get())
+	    continue;
+
+	  int num_reg = surfaces_[ki]->numRegions();
+	  if (num_reg == 0)
+	    continue; // Should not happen
+
+	  RevEngRegion *reg0 = surfaces_[ki]->getRegion(0);
+	  if (reg0->hasBlendEdge())
+	    continue;  // Derived information
+      
+	  if (reg0->hasAssociatedBlend())
+	    continue;  // Outdated information
+      
+	  int num_pts = surfaces_[ki]->numPoints();
+	  if (num_pts < min_num)
+	    continue;
+
+	  Point vec = curr->direction();
+	  int ix = -1;
+	  double min_angle0 = std::numeric_limits<double>::max();
+	  for (int ka=0; ka<3; ++ka)
+	    {
+	      double ang = vec.angle(axis0[ka]);
+	      ang = std::min(ang, M_PI-ang);
+	      if (ang < min_angle0)
+		{
+		  min_angle0 = ang;
+		  ix = ka;
+		}
+	      if (ang < max_ang)
+		{
+		  for (int kb=0; kb<num_reg; ++kb)
+		    {
+		      RevEngRegion *reg = surfaces_[ki]->getRegion(kb);
+		      points[ka].push_back(std::make_pair(reg->pointsBegin(),
+							  reg->pointsEnd()));
+		    }
+		  num_points[ka] += num_pts;
+		}
+	    }
+	  if (ix >= 0)
+	    {
+	      min_angle[ix] = min_angle0;
+	      min_axis[ix] = vec;
+	    }
+	}
+      if (num_points[0]+num_points[1]+num_points[2] == 0 && kc == 0)
+	{
+	  for (int kb=0; kb<3; ++kb)
+	    if (min_axis[kb].length() > 0.0)
+	      axis0[kb] = min_axis[kb];
+	}
+      else if (kc == 0)
+	break;
+    }
+
+  Point Cx, Cy;
+  for (int ka=0; ka<3; ++ka)
+    {
+      if (points[ka].size() > 0)
+	{
+	  RevEngUtils::computeAxis(points[ka], axis[ka], Cx, Cy);
+	}
+      else
+	axis[ka] = initaxis[ka];
+    }
+
+}
+
+//===========================================================================
+void RevEng::computeAxisFromPlane(Point initaxis[3], int min_num, double max_ang,
+				  Point axis[3], int num_points[3])
+//===========================================================================
+{
+  vector<vector<shared_ptr<Plane> > > planes(3);
+  vector<vector<int> > num(3);
+  vector<vector<double> > avd(3);
+  num_points[0] =  num_points[1] = num_points[2] = 0;
+  Point axis0[3];
+  for (int ka=0; ka<3; ++ka)
+    axis0[ka] = initaxis[ka];
+  double max_ang2 = 0.5*M_PI - 1.0e-9;
+  for (int kc=0; kc<2; ++kc)
+    {
+      Point dummy(0.0, 0.0, 0.0);
+      vector<double> min_angle(3, max_ang2);
+      vector<Point> min_axis(3, dummy);
+      for (size_t ki=0; ki<surfaces_.size(); ++ki)
+	{
+	  int sfcode;
+	  ClassType type = surfaces_[ki]->instanceType(sfcode);
+	  if (type != Class_Plane)
+	    continue;
+
+	  shared_ptr<Plane> curr =
+	    dynamic_pointer_cast<Plane,ParamSurface>(surfaces_[ki]->surface());
+	  if (!curr.get())
+	    continue;
+
+	  int num_pts = surfaces_[ki]->numPoints();
+	  if (num_pts < min_num)
+	    continue;
+      
+	  // Check for a more accurate base surface
+	  double avdist = 0.0;
+	  int num_reg = surfaces_[ki]->numRegions();
+	  double fac = 1.0/(double)num_pts;
+	  if (num_reg > 0)
+	    {
+	      // Should always be the case
+	      RevEngRegion* reg = surfaces_[ki]->getRegion(0);
+	      shared_ptr<ParamSurface> base = reg->getBase();
+	      if (base.get() && base->instanceType() == Class_Plane)
+		{
+		  double av1 = 0.0, av2 = 0.0;
+		  //int nmb = 0;
+		  for (int ka=0; ka<num_reg; ++ka)
+		    {
+		      reg = surfaces_[ki]->getRegion(ka);
+		      double maxds, avds, maxdb, avdb;
+		      int num_in, num2_in, num_inb, num2_inb;
+		      reg->getAccuracy(maxds, avds, num_in, num2_in);
+		      reg->getBaseDist(maxdb, avdb, num_inb, num2_inb);
+		      int nn = reg->numPoints();
+		      av1 += (double)nn*fac*avds;
+		      av2 += (double)nn*fac*avdb;
+		    }
+		  if (av2 < av1)
+		    {
+		      curr = dynamic_pointer_cast<Plane,ParamSurface>(base);
+		      avdist = av2;
+		    }
+		  else
+		    avdist = av1;
+		}
+	    }
+
+	  Point normal = curr->direction();
+	  int ix = -1;
+	  double min_angle0 = std::numeric_limits<double>::max();
+	  for (int kb=0; kb<3; ++kb)
+	    {
+	      double ang = normal.angle(axis0[kb]);
+	      ang = std::min(ang, M_PI-ang);
+	      if (ang < min_angle0)
+		{
+		  min_angle0 = ang;
+		  ix = kb;
+		}
+	      if (ang <= max_ang)
+		{
+		  planes[kb].push_back(curr);
+		  num[kb].push_back(num_pts);
+		  avd[kb].push_back(avdist);
+		  num_points[kb] += num_pts;
+		}
+	    }
+	  if (ix >= 0)
+	    {
+	      min_angle[ix] = min_angle0;
+	      min_axis[ix] = normal;
+	    }
+	}
+      if (num_points[0]+num_points[1]+num_points[2] == 0 && kc == 0)
+	{
+	  for (int kb=0; kb<3; ++kb)
+	    if (min_axis[kb].length() > 0.0)
+	      axis0[kb] = min_axis[kb];
+	}
+      else if (kc == 0)
+	break;
+    }
+  
+  for (int kb=0; kb<3; ++kb)
+    {
+      if (planes[kb].size() == 0)
+	{
+	  axis[kb] = initaxis[kb];
+	  continue;
+	}
+
+      axis[kb] = Point(0.0, 0.0, 0.0);
+      double fac = 1.0/(double)num_points[kb];
+      for (size_t ki=0; ki<planes[kb].size(); ++ki)
+	{
+	  Point normal = planes[kb][ki]->direction();
+	  if (normal*axis0[kb] < 0.0)
+	    normal *= -1.0;
+
+	  double wgt = fac*(1.0 - avd[kb][ki])*num[kb][ki];
+	  wgt = std::max(wgt, 0.0);
+	  axis[kb] += wgt*normal;
+	}
+      axis[kb].normalize_checked();
+    }
+}
+
+//===========================================================================
+bool RevEng::recognizeOneSurface(int& ix, int min_point_in, double angtol,
+				 int pass)
+//===========================================================================
+{
+  bool firstpass = (pass <= 2);
+  if (regions_[ix]->tryOtherSurf(prefer_elementary_, firstpass) /*&&
+								  regions_[ix]->feasiblePlane(zero_H_, zero_K_)*/)
+    {
+      vector<shared_ptr<HedgeSurface> > plane_sfs;
+      vector<HedgeSurface*> prev_surfs;
+      vector<vector<RevEngPoint*> > out_groups;
+      bool found = regions_[ix]->extractPlane(mainaxis_,
+					      approx_tol_, min_point_in,
+					      min_point_region_, angtol, 
+					      prefer_elementary_,
+					      plane_sfs, prev_surfs, out_groups);
+
+      if (out_groups.size() > 0 || prev_surfs.size() > 0)
+	surfaceExtractOutput(ix, out_groups, prev_surfs);
+
+      if (plane_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), plane_sfs.begin(), plane_sfs.end());
+      if (found && regions_[ix]->getMaxSfDist() < 0.5*approx_tol_)
+	return true;  // Result accepted
+    }
+
+  if (regions_[ix]->tryOtherSurf(prefer_elementary_, firstpass))
+    {
+      vector<shared_ptr<HedgeSurface> > cyl_sfs;
+      vector<HedgeSurface*> prev_surfs;
+      vector<vector<RevEngPoint*> > out_groups;
+      bool repeat = false;
+      
+      // Cylinder from context information
+      bool found =
+	regions_[ix]->contextCylinder(mainaxis_, approx_tol_,
+				      min_point_in, min_point_region_, 
+				      angtol, prefer_elementary_,
+				      cyl_sfs, prev_surfs,
+				      out_groups);
+
+      if (found == false && regions_[ix]->feasibleCylinder(zero_H_, zero_K_))
+	(void)regions_[ix]->extractCylinder(approx_tol_, min_point_in,
+					    min_point_region_, angtol,
+					    prefer_elementary_,
+					    cyl_sfs, prev_surfs,
+					    out_groups, repeat);
+
+      
+      if (out_groups.size() > 0 || prev_surfs.size() > 0)
+	surfaceExtractOutput(ix, out_groups, prev_surfs);
+	
+      if (repeat)
+	{
+	  --ix;
+	  return false;  // New try
+	}
+	
+
+      if (cyl_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), cyl_sfs.begin(), cyl_sfs.end());
+      if (found && regions_[ix]->getMaxSfDist() < 0.5*approx_tol_)
+	return true;
+    }
+  
+  if (regions_[ix]->tryOtherSurf(prefer_elementary_, firstpass) && (!firstpass) &&
+      regions_[ix]->hasSweepInfo() && regions_[ix]->sweepType() == 1)
+    {
+      vector<shared_ptr<HedgeSurface> > linsweep_sfs;
+      vector<HedgeSurface*> prev_surfs;
+      vector<vector<RevEngPoint*> > out_groups;
+      bool found = regions_[ix]->extractLinearSweep(approx_tol_, min_point_in,
+						 min_point_region_, angtol, 
+						 prefer_elementary_,
+						 linsweep_sfs, 
+						 prev_surfs);
+      if (out_groups.size() > 0 || prev_surfs.size() > 0)
+	surfaceExtractOutput(ix, out_groups, prev_surfs);
+	
+      if (linsweep_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), linsweep_sfs.begin(), linsweep_sfs.end());
+	   
+      if (found && regions_[ix]->getMaxSfDist() < 0.5*approx_tol_)
+	return true;
+    }
+      
+  if (regions_[ix]->tryOtherSurf(prefer_elementary_, firstpass) && pass > 1)
+    {
+      vector<shared_ptr<HedgeSurface> > tor_sfs;
+      vector<HedgeSurface*> prev_surfs;
+      vector<vector<RevEngPoint*> > out_groups;
+
+      // Torus from context information
+      bool found =  regions_[ix]->contextTorus(mainaxis_, approx_tol_,
+					       min_point_in, min_point_region_, 
+					       angtol, prefer_elementary_,
+					       tor_sfs, prev_surfs,
+					       out_groups);
+      if (regions_[ix]->numPoints() == 0)
+	{
+	  regions_.erase(regions_.begin()+ix);
+	  --ix;
+	  return false;
+	}
+
+      if (!found)
+	found = regions_[ix]->extractTorus(mainaxis_, approx_tol_, min_point_in,
+					   min_point_region_,
+					   angtol, prefer_elementary_,
+					   tor_sfs, prev_surfs,
+					   out_groups);
+
+      if (out_groups.size() > 0 || prev_surfs.size() > 0)
+	surfaceExtractOutput(ix, out_groups, prev_surfs);
+
+      if (tor_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), tor_sfs.begin(), tor_sfs.end());
+      if (found && regions_[ix]->getMaxSfDist() < 0.5*approx_tol_)
+	return true;
+    }
+
+  if (regions_[ix]->tryOtherSurf(prefer_elementary_, firstpass) && pass > 1)
+    {
+      vector<shared_ptr<HedgeSurface> > sph_sfs;
+      vector<HedgeSurface*> prev_surfs;
+      vector<vector<RevEngPoint*> > out_groups;
+      bool found = regions_[ix]->extractSphere(mainaxis_, approx_tol_,
+					       min_point_in, min_point_region_,
+					       angtol, prefer_elementary_,
+					       sph_sfs, prev_surfs,
+					       out_groups);
+
+      
+      if (out_groups.size() > 0 || prev_surfs.size() > 0)
+	surfaceExtractOutput(ix, out_groups, prev_surfs);
+	
+      if (sph_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), sph_sfs.begin(), sph_sfs.end());
+      if (found && regions_[ix]->getMaxSfDist() < 0.5*approx_tol_)
+	return true;
+    }
+
+  if (regions_[ix]->tryOtherSurf(prefer_elementary_, firstpass) && pass > 1)
+    {
+      vector<shared_ptr<HedgeSurface> > cone_sfs;
+      vector<HedgeSurface*> prev_surfs;
+      vector<vector<RevEngPoint*> > out_groups;
+      bool found =
+	regions_[ix]->extractCone(approx_tol_, min_point_in, min_point_region_, 
+				  angtol, prefer_elementary_,
+				  cone_sfs, prev_surfs,
+				  out_groups);
+	   
+      if (out_groups.size() > 0 || prev_surfs.size() > 0)
+	surfaceExtractOutput(ix, out_groups, prev_surfs);
+	
+      if (cone_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), cone_sfs.begin(), cone_sfs.end());
+      if (found && regions_[ix]->getMaxSfDist() < 0.5*approx_tol_)
+	return true;
+    }
+
+  if (regions_[ix]->tryOtherSurf(prefer_elementary_, firstpass) && (!firstpass)) 
+    {
+      vector<shared_ptr<HedgeSurface> > spl_sfs;
+      vector<HedgeSurface*> prev_surfs;
+      vector<vector<RevEngPoint*> > out_groups;
+      bool found = 
+	regions_[ix]->extractFreeform(approx_tol_, min_point_in,
+				      min_point_region_, angtol,
+				      prefer_elementary_,
+				      spl_sfs, prev_surfs,
+				      out_groups);
+
+      if (out_groups.size() > 0 || prev_surfs.size() > 0)
+	surfaceExtractOutput(ix, out_groups, prev_surfs);
+
+      if (spl_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), spl_sfs.begin(), spl_sfs.end());
+    }
+
+  if (!regions_[ix]->hasSurface())
+    {
+      vector<shared_ptr<HedgeSurface> > adj_sfs;
+      vector<HedgeSurface*> prev_surfs;
+      vector<vector<RevEngPoint*> > out_groups;
+       bool found =
+	regions_[ix]->adjacentToCylinder(mainaxis_, approx_tol_, min_point_in,
+					 min_point_region_, angtol,
+					 prefer_elementary_,
+					 adj_sfs, prev_surfs,
+					 out_groups);
+      
+      if (out_groups.size() > 0 || prev_surfs.size() > 0)
+	surfaceExtractOutput(ix, out_groups, prev_surfs);
+
+      if (adj_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), adj_sfs.begin(), adj_sfs.end());
+    }
+#ifdef DEBUG
+  if (regions_[ix]->hasSurface())
+    {
+      std::ofstream of("one_surface.g2");
+      regions_[ix]->writeSurface(of);
+    }
+#endif
+  return (regions_[ix]->hasSurface());
+}
+
+//===========================================================================
+void RevEng::recognizeSurfaces(int min_point_in, int pass)
+//===========================================================================
+{
+  double angfac = 5.0;
+  double angtol = angfac*anglim_;
+  int pass2 = pass + 1;
+  std::sort(regions_.begin(), regions_.end(), sort_region);
+#ifdef DEBUG
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, pre recognizeSurfaces. " << ki << " " << tmpset.size() << " " << regions_[ki]->numPoints() << std::endl;
+      int num = regions_[ki]->numPoints();
+      for (int ka=0; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+	  std::cout << "Inconsistent region pointers, pre recognizeSurfaces: " << ki << " " << ka << std::endl;
+    }
+#endif
+
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+#ifdef DEBUG_CHECK
+      std::set<RevEngPoint*> tmpset(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, pre. " << ki << " " << tmpset.size() << " " << regions_[ki]->numPoints() << std::endl;
+      
+ #endif
+    }
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+#ifdef DEBUG_CHECK
+      std::set<RevEngPoint*> tmpset(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch. " << ki << " " << tmpset.size() << " " << regions_[ki]->numPoints() << std::endl;
+      for (size_t kj=0; kj<regions_.size(); ++kj)
+	{
+	  int nump = regions_[kj]->numPoints();
+	  for (int ka=0; ka<nump; ++ka)
+	    if (regions_[kj]->getPoint(ka)->region() != regions_[kj].get())
+	      std::cout << "Inconsistent region pointers, recognizeSurfaces: " << ki << " " << kj << " " << ka << std::endl;
+	}
+#endif
+      if (regions_[ki]->numPoints() < min_point_region_)
+	continue;
+      if (regions_[ki]->hasAssociatedBlend())
+	continue;  // Treated separately
+#ifdef DEBUGONE
+      //int classtype = regions_[ki]->getClassification();
+      //std::cout << "No " << ki <<  ", classtype: " << classtype << std::endl;
+      
+      std::ofstream of1("region.g2");
+      regions_[ki]->writeRegionInfo(of1);
+      if (regions_[ki]->hasSurface())
+	regions_[ki]->writeSurface(of1);
+      std::ofstream of2("unitsphere.g2");
+      regions_[ki]->writeUnitSphereInfo(of2);
+#endif
+      if (regions_[ki]->hasBlendEdge())
+	continue;
+      if (regions_[ki]->hasRevEdges())
+	continue;
+      if (regions_[ki]->getAdaptionHistory() > INITIAL)
+	continue; // Do not redo
+      
+      bool pot_blend = false;
+      if (!regions_[ki]->hasSurface())
+	pot_blend = regions_[ki]->potentialBlend(angtol);
+      if (pot_blend)
+	{
+#ifdef DEBUG
+	  std::cout << "Potential blend" << std::endl;
+#endif
+	  bool done = setBlendEdge(ki);
+	  if (done)
+	    continue;
+	}
+      bool found = recognizeOneSurface(ki, min_point_in, angtol, pass2);
+#ifdef DEBUG_CHECK
+      bool connect = regions_[ki]->isConnected();
+      if (!connect)
+	std::cout << "recognizeOneSurface, disconnected region " << ki << std::endl;
+#endif
+        
+#ifdef DEBUG_CHECK
+  for (int kj=0; kj<(int)regions_.size(); ++kj)
+    {
+      std::set<RevEngPoint*> tmpset2(regions_[kj]->pointsBegin(), regions_[kj]->pointsEnd());
+      if ((int)tmpset2.size() != regions_[kj]->numPoints())
+	std::cout << "Point number mismatch 2. " << kj << " " << ki << " " << tmpset2.size() << " " << regions_[kj]->numPoints() << std::endl;
+    }
+#endif
+  if ((!regions_[ki]->hasSurface()) ||
+      (regions_[ki]->hasSurface() &&
+       (regions_[ki]->getSurfaceFlag() == ACCURACY_POOR ||
+	regions_[ki]->getSurfaceFlag() == NOT_SET)))
+	{
+	  // Still no surface. Try to divide composite regions into smaller
+	  // pieces
+	  bool split = segmentComposite(ki, min_point_in, angtol);
+#ifdef DEBUG_CHECK
+	  if (ki >= 0)
+	    {
+	      bool connect = regions_[ki]->isConnected();
+	      if (!connect)
+		std::cout << "segmentComposite, disconnected region " << ki << std::endl;
+	    }
+#endif
+	  int stop_split = 1;
+	}
+   }
+
+#ifdef DEBUG
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, post recognizeSurfaces. " << ki << " " << tmpset.size() << " " << regions_[ki]->numPoints() << std::endl;
+      int num = regions_[ki]->numPoints();
+      for (int ka=0; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+	  std::cout << "Inconsistent region pointers, post recognizeSurfaces: " << ki << " " << ka << std::endl;
+    }
+#endif
+
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+}
+
+
+//===========================================================================
+bool RevEng::segmentComposite(int& ix, int min_point_in, double angtol)
+//===========================================================================
+{
+  // To be updated
+  double frac_norm_lim = 0.2; //0.75;
+  bool segmented = false;
+  
+  if (regions_[ix]->numPoints() < min_point_region_)
+    return false;
+
+  if (regions_[ix]->hasSurface() &&
+      (!(regions_[ix]->getSurfaceFlag() == ACCURACY_POOR ||
+	 regions_[ix]->getSurfaceFlag() == NOT_SET)))
+    return false;
+
+  if (regions_[ix]->hasSweepInfo())
+    return false;
+
+#ifdef DEBUG_DIV
+  std::cout << "Segment composite, ix=" << ix << std::endl;
+  std::ofstream ofs("region_to_segm.g2");
+  regions_[ix]->writeRegionInfo(ofs);
+  std::ofstream of2("unitsphere_segm.g2");
+  regions_[ix]->writeUnitSphereInfo(of2);
+  Point origo(0.0, 0.0, 0.0);
+  of2 << "410 1 0 4 0 0 0 255" << std::endl;
+  of2 << "3" << std::endl;
+  for (int ka=0; ka<3; ++ka)
+    of2 << origo << " " << mainaxis_[ka] << std::endl;
+
+  std::ofstream ofa("adjacent_to_segm.g2");
+  regions_[ix]->writeAdjacentPoints(ofa);
+  //regions_[ix]->sortByAxis(mainaxis_, min_point_in, approx_tol_);
+#endif
+
+  size_t num_points = regions_[ix]->numPoints();
+  
+  bool plane_grow = false;
+  if (plane_grow && regions_[ix]->getFracNorm() > frac_norm_lim)
+    {
+      // Grow sub regions according to surface type
+#ifdef DEBUG_DIV
+      //std::cout << "Grow planes" << std::endl;
+#endif
+      segmented = segmentByPlaneGrow(ix, min_point_in, angtol);
+#ifdef DEBUG_DIV
+      if (segmented)
+	std::cout << "Segmented by grow planes" << std::endl;
+#endif
+    }
+
+  bool repeat = false;
+  vector<shared_ptr<HedgeSurface> > hedgesfs;
+  vector<shared_ptr<RevEngRegion> > added_reg;
+  vector<vector<RevEngPoint*> > separate_groups;
+  vector<RevEngPoint*> single_points;
+  vector<HedgeSurface*> prevsfs;
+
+  if (!segmented && regions_[ix]->hasDivideInfo())
+    {
+      for (int ki=0; ki<regions_[ix]->numDivideInfo(); ++ki)
+	{
+	  segmented = regions_[ix]->divideWithSegInfo(ki,
+						      min_point_region_,
+						      separate_groups,
+						      single_points);
+	  if (segmented)
+	    break;
+	}
+    }
+  
+  if (!segmented)
+    {
+      // Check if a segmentation into several cylinder like
+      // regions is feasible
+      double avH, avK, MAH, MAK;
+      regions_[ix]->getAvCurvatureInfo(avH, avK, MAH, MAK);
+      double fac = 5.0;
+      if (MAH > fac*MAK)
+	{
+	  segmented = regions_[ix]->extractCylByAxis(mainaxis_, min_point_in,
+						     min_point_region_,
+						     approx_tol_, angtol,
+						     prefer_elementary_,
+						     hedgesfs, added_reg,
+						     separate_groups,
+						     single_points);
+	}
+    }
+
+#ifdef DEBUG_CHECK
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset1(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset1.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, composite 1. " << ki << " " << tmpset1.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+#endif
+    
+  if (!segmented)
+    {
+      vector<RevEngRegion*> adj_planar = regions_[ix]->fetchAdjacentPlanar();
+      if (adj_planar.size() > 1)
+	{
+	  segmented = regions_[ix]->segmentByPlaneAxis(mainaxis_, min_point_in,
+						       min_point_region_,
+						       approx_tol_, angtol,
+						       prefer_elementary_,
+						       adj_planar, hedgesfs,
+						       added_reg,
+						       prevsfs, separate_groups);
+#ifdef DEBUG_CHECK
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset2(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset2.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, composite 2. " << ki << " " << tmpset2.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+    
+  for (size_t kr=0; kr<adj_planar.size(); ++kr)
+    {
+      std::set<RevEngPoint*> tmpset(adj_planar[kr]->pointsBegin(),
+				    adj_planar[kr]->pointsEnd());
+      if ((int)tmpset.size() != adj_planar[kr]->numPoints())
+	std::cout << "Point number mismatch (ByPlaneAxis). " << kr << " " << tmpset.size() << " " << adj_planar[kr]->numPoints() << std::endl;
+    }
+	  
+#endif
+	}
+      
+      if (!segmented)
+	{
+	  // Extend with cylindrical
+	  vector<RevEngRegion*> adj_cyl =
+	    regions_[ix]->fetchAdjacentCylindrical();
+	  if (adj_cyl.size() > 0)
+	    adj_planar.insert(adj_planar.end(), adj_cyl.begin(),
+			      adj_cyl.end());
+	  if (adj_planar.size() > 0)
+	    segmented =
+	      regions_[ix]->segmentByAdjSfContext(mainaxis_, min_point_in,
+						  min_point_region_,
+						  approx_tol_, angtol,
+						  adj_planar, separate_groups);
+#ifdef DEBUG_CHECK
+	  for (int ki=0; ki<(int)regions_.size(); ++ki)
+	    {
+	      std::set<RevEngPoint*> tmpset4(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+	      if ((int)tmpset4.size() != regions_[ki]->numPoints())
+		std::cout << "Point number mismatch, composite 4. " << ki << " " << tmpset4.size() << " " << regions_[ki]->numPoints() << std::endl;
+	    }
+	  for (size_t kr=0; kr<adj_planar.size(); ++kr)
+	    {
+	      std::set<RevEngPoint*> tmpset(adj_planar[kr]->pointsBegin(),
+					    adj_planar[kr]->pointsEnd());
+	      if ((int)tmpset.size() != adj_planar[kr]->numPoints())
+		std::cout << "Point number mismatch (ByAdjSfContext). " << kr << " " << tmpset.size() << " " << adj_planar[kr]->numPoints() << std::endl;
+	    }
+#endif
+	}
+    }
+  
+  if (added_reg.size() > 0)
+    repeat = true;
+  else if (separate_groups.size() > 0)
+    {
+      int num = 0;
+      for (size_t ki=0; ki<separate_groups.size(); ++ki)
+	num += (int)separate_groups[ki].size();
+      if (num > (int)num_points/10)
+	repeat = true;
+    }
+  
+#ifdef DEBUG_CHECK
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset5(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset5.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, composite 5. " << ki << " " << tmpset5.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+#endif
+#ifdef DEBUG
+  if (regions_[ix]->numPoints() < (int)num_points)
+    {
+      std::ofstream of("seg_by_context.g2");
+      int num = regions_[ix]->numPoints();
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of <<  num << std::endl;
+      for (int ka=0; ka<num; ++ka)
+	of << regions_[ix]->getPoint(ka)->getPoint() << std::endl;
+
+      for (size_t ki=0; ki<separate_groups.size(); ++ki)
+	{
+	  of << "400 1 0 4 0 255 0 255" << std::endl;
+	  of <<  separate_groups[ki].size() << std::endl;
+	  for (int ka=0; ka<(int)separate_groups[ki].size(); ++ka)
+	    of << separate_groups[ki][ka]->getPoint() << std::endl;
+	}
+      
+      for (size_t ki=0; ki<added_reg.size(); ++ki)
+	{
+	  int num = added_reg[ki]->numPoints();
+	  of << "400 1 0 4 0 0 255 255" << std::endl;
+	  of <<  num << std::endl;
+	  for (int ka=0; ka<num; ++ka)
+	    of << added_reg[ki]->getPoint(ka)->getPoint() << std::endl;
+	  if (added_reg[ki]->hasSurface())
+	    added_reg[ki]->writeSurface(of);
+	}
+    }
+#endif
+
+  // Update adjacency information for current region
+  regions_[ix]->updateRegionAdjacency();
+  
+#ifdef DEBUG_CHECK
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset6(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset6.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, composite 6. " << ki << " " << tmpset6.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+#endif
+  if (added_reg.size() > 0 && regions_[ix]->hasSurface() == false)
+    {
+      // Swap sequence of regions
+      int max_pt = 0;
+      int max_ix = -1;
+      for (size_t ki=0; ki<added_reg.size(); ++ki)
+	if (added_reg[ki]->numPoints() > max_pt)
+	  {
+	    max_pt = added_reg[ki]->numPoints();
+	    max_ix = (int)ki;
+	  }
+      if (max_ix >= 0)
+	std::swap(regions_[ix], added_reg[max_ix]);
+    }
+#ifdef DEBUG_CHECK
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset6_2(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset6_2.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, composite 6_2. " << ki << " " << tmpset6_2.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+#endif
+  if (separate_groups.size() > 0)
+    {
+      vector<HedgeSurface*> prev_surfs;
+      surfaceExtractOutput(ix, separate_groups, prev_surfs);
+    }
+
+#ifdef DEBUG_CHECK
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      std::set<RevEngPoint*> tmpset7(regions_[ki]->pointsBegin(), regions_[ki]->pointsEnd());
+      if ((int)tmpset7.size() != regions_[ki]->numPoints())
+	std::cout << "Point number mismatch, composite 7. " << ki << " " << tmpset7.size() << " " << regions_[ki]->numPoints() << std::endl;
+    }
+  if (!regions_[ix]->isConnected())
+    std::cout << "Disconnected region (split), ix= " << ix << " " << regions_[ix].get() << std::endl;
+#endif
+  if (added_reg.size() > 0)
+    regions_.insert(regions_.end(), added_reg.begin(), added_reg.end());
+  if (hedgesfs.size() > 0)
+    surfaces_.insert(surfaces_.end(), hedgesfs.begin(), hedgesfs.end());
+  if (repeat && (!regions_[ix]->hasSurface()))
+    {
+#ifdef DEBUG_DIV
+      std::cout << "Repeat ix = " << ix << std::endl;
+#endif
+      --ix;
+    }
+  
+  return segmented;
+}
+
+//===========================================================================
+void RevEng::adjustPointRegions(int min_point_in)
+//===========================================================================
+{
+  double angfac = 5.0;
+  double angtol = angfac*anglim_;
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      if (regions_[ki]->hasSurface())
+	continue;
+      if (regions_[ki]->hasAssociatedBlend())
+	continue;
+      if (regions_[ki]->numPoints() < min_point_in)
+	continue;
+      
+      vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*>  > adj_elem, adj_elem_base;
+      regions_[ki]->getAdjacentElemInfo(adj_elem, adj_elem_base);
+      if (adj_elem.size() == 0)
+	continue;
+
+      vector<RevEngRegion*> adj_groups(adj_elem.size());
+      for (size_t kj=0; kj<adj_elem.size(); ++kj)
+	adj_groups[kj] = adj_elem[kj].second;
+
+      vector<vector<RevEngPoint*> > out_groups;
+      (void)regions_[ki]->segmentByAdjSfContext(mainaxis_, min_point_in,
+						min_point_region_,
+						approx_tol_,
+						angtol, adj_groups,
+						out_groups);
+      if (regions_[ki]->numPoints() == 0)
+	{
+	  vector<RevEngRegion*> adjacent;
+	  regions_[ki]->getAdjacentRegions(adjacent);
+	  for (size_t kj=0; kj<adjacent.size(); ++kj)
+	    adjacent[kj]->removeAdjacentRegion(regions_[ki].get());
+
+	  regions_.erase(regions_.begin()+ki);
+	  --ki;
+	}
+      
+      if (out_groups.size() > 0)
+	{
+	  vector<HedgeSurface*> dummy;
+	  surfaceExtractOutput((int)ki, out_groups, dummy);
+	}
+
+    }
+}
+
+//===========================================================================
+void RevEng::surfaceCreation(int pass)
+//===========================================================================
+{
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+  
+  int min_point_in = 50; //10; //20;
+  adjustPointRegions(min_point_in);
+  
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+  
+  // First pass. Recognize elementary surfaces
+  recognizeSurfaces(min_point_in, pass);
+
+#ifdef DEBUG
+  checkConsistence("Regions9");
+
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions9" << std::endl;
+      std::ofstream of("regions9.g2");
+      std::ofstream ofm("mid_regions9.g2");
+      std::ofstream ofs("small_regions9.g2");
+      writeRegionStage(of, ofm, ofs);
+      std::ofstream of0("regions9_helix.g2");
+      for (int ki=0; ki<(int)regions_.size(); ++ki)
+	{
+	  if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+	    {
+	      regions_[ki]->writeRegionInfo(of0);
+	      if (regions_[ki]->hasSurface())
+		regions_[ki]->writeSurface(of0);
+	    }
+	}
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf9.g2");
+	  writeRegionWithSurf(of);
+	}
+   
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges9.g2");
+       writeEdgeStage(ofe);
+     }
+
+   std::cout << "Merge adjacent regions, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+#endif
+   double angtol = 5.0*anglim_;
+   for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      if (regions_[ki]->hasSurface())
+	{
+	  vector<RevEngRegion*> grown_regions;
+	  vector<HedgeSurface*> adj_surfs;
+	  vector<RevEngEdge*> adj_edgs;
+	  regions_[ki]->mergeAdjacentSimilar(approx_tol_, angtol,
+					     grown_regions, adj_surfs, adj_edgs);
+	if (grown_regions.size() > 0 || adj_surfs.size() > 0)
+	  updateRegionsAndSurfaces(ki, grown_regions, adj_surfs);
+	for (size_t kr=0; kr<adj_edgs.size(); ++kr)
+	  {
+	    size_t kj;
+	    for (kj=0; kj<edges_.size(); ++kj)
+	      if (edges_[kj].get() == adj_edgs[kr])
+		break;
+	    if (kj < edges_.size())
+	      edges_.erase(edges_.begin()+kj);
+	  }
+	}
+    }
+#ifdef DEBUG
+  checkConsistence("Regions10");
+
+   std::cout << "Finished merge adjacent regions, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions10" << std::endl;
+      std::ofstream of("regions10.g2");
+      std::ofstream ofm("mid_regions10.g2");
+      std::ofstream ofs("small_regions10.g2");
+      writeRegionStage(of, ofm, ofs);
+      std::ofstream of0("regions10_helix.g2");
+      for (int ki=0; ki<(int)regions_.size(); ++ki)
+	{
+	  if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+	    {
+	      regions_[ki]->writeRegionInfo(of0);
+	      if (regions_[ki]->hasSurface())
+		regions_[ki]->writeSurface(of0);
+	    }
+	}
+        if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf10.g2");
+	  writeRegionWithSurf(of);
+	}
+     }
+   
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges10.g2");
+       writeEdgeStage(ofe);
+     }
+#endif
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+#ifdef DEBUG_CHECK
+  for (int ka=0; ka<(int)regions_.size(); ++ka)
+    {
+      std::set<RevEngPoint*> tmpset(regions_[ka]->pointsBegin(), regions_[ka]->pointsEnd());
+      if ((int)tmpset.size() != regions_[ka]->numPoints())
+	std::cout << "Point number mismatch, pre grow. " << ka << " " << tmpset.size() << " " << regions_[ka]->numPoints() << std::endl;
+
+      bool con = regions_[ka]->isConnected();
+      if (!con)
+	std::cout << "Disconnected region, ka= " << ka << " " << regions_[ka].get() << std::endl;
+    }
+#endif
+
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      if (regions_[ki]->hasSurface() && regions_[ki]->getSurfaceFlag() < ACCURACY_POOR)
+	{
+#ifdef DEBUG_GROW
+      std::cout << "ki=" << ki << ", nmb reg: " << regions_.size() << ", nmb surf: " << surfaces_.size() << std::endl;
+#endif
+      growSurface(ki, pass);
+#ifdef DEBUG_CHECK
+  if (!regions_[ki]->isConnected())
+    std::cout << "Disconnected region (grow), ki= " << ki << " " << regions_[ki].get() << std::endl;
+#endif
+	}
+      // for (int kh=0; kh<(int)surfaces_.size(); ++kh)
+      // 	{
+      // 	  int numreg = surfaces_[kh]->numRegions();
+      // 	  for (int ka=0; ka<numreg; ++ka)
+      // 	    {
+      // 	      RevEngRegion *reg = surfaces_[kh]->getRegion(ka);
+      // 	      size_t kr;
+      // 	      for (kr=0; kr<regions_.size(); ++kr)
+      // 		if (reg == regions_[kr].get())
+      // 		  break;
+      // 	      if (kr == regions_.size())
+      // 		std::cout << "Region4, surface 1. Obsolete region pointer, ki=" << ki << ", kh=" << kh << ". Region: " << reg << ", surface: " << surfaces_[kh].get() << std::endl;
+      // 	    }
+      // 	}
+    }
+      
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+#ifdef DEBUG
+  checkConsistence("Regions11");
+
+   std::cout << "Finished grow with surf, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions11" << std::endl;
+      std::ofstream of("regions11.g2");
+      std::ofstream ofm("mid_regions11.g2");
+      std::ofstream ofs("small_regions11.g2");
+      writeRegionStage(of, ofm, ofs);
+      std::ofstream of0("regions11_helix.g2");
+      for (int ki=0; ki<(int)regions_.size(); ++ki)
+	{
+	  if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+	    {
+	      regions_[ki]->writeRegionInfo(of0);
+	      if (regions_[ki]->hasSurface())
+		regions_[ki]->writeSurface(of0);
+	    }
+	}
+        if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11.g2");
+	  writeRegionWithSurf(of);
+	}
+     }
+   
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges11.g2");
+       writeEdgeStage(ofe);
+     }
+#endif
+}
+
+//===========================================================================
+void RevEng::manageBlends1()
+//===========================================================================
+{
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  
+#ifdef DEBUG
+  std::ofstream ofu1("unresolved3.g2");
+  for (size_t kr=0; kr<regions_.size(); ++kr)
+    {
+      if (regions_[kr]->hasSurface())
+	continue;
+      if (regions_[kr]->hasAssociatedBlend())
+	continue;
+      regions_[kr]->writeRegionPoints(ofu1);
+    }
+#endif
+
+#ifdef DEBUG
+  if (edges_.size() > 0)
+    {
+      std::ofstream ofe("edges10.g2");
+      writeEdgeStage(ofe);
+    }
+#endif
+  
+#ifdef DEBUG
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      int num = regions_[ki]->numPoints();
+      for (int ka=0; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+	  std::cout << "Inconsistent region pointers, pre createBlendSurface: " << ki << " " << ka << std::endl;
+    }
+#endif
+  
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+#ifdef DEBUG
+   std::cout << "Create blends" << std::endl;
+#endif
+   for (size_t ki=0; ki<edges_.size(); ++ki)
+     (void)createBlendSurface((int)ki);
+
+   
+#ifdef DEBUG
+    std::cout << "Finished create blends, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions11_2" << std::endl;
+      std::ofstream of("regions11_2.g2");
+      std::ofstream ofm("mid_regions11_2.g2");
+      std::ofstream ofs("small_regions11_2.g2");
+      writeRegionStage(of, ofm, ofs);
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11_2.g2");
+	  writeRegionWithSurf(of);
+	}
+     }
+   
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges11_2.g2");
+       writeEdgeStage(ofe);
+     }
+#endif
+
+   // Update blend radii
+   equalizeBlendRadii();
+   
+#ifdef DEBUG
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions11_3_1" << std::endl;
+      std::ofstream of("regions11_3_1.g2");
+      std::ofstream ofm("mid_regions11_3_1.g2");
+      std::ofstream ofs("small_regions11_3_1.g2");
+      writeRegionStage(of, ofm, ofs);
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11_4.g2");
+	  writeRegionWithSurf(of);
+	}
+    }
+#endif
+   int stop_break = 1;
+}
+
+//===========================================================================
+void RevEng::manageBlends2()
+//===========================================================================
+{
+#ifdef DEBUG
+   std::cout << "Set blend boundaries" << std::endl;
+#endif
+   for (size_t ki=0; ki<surfaces_.size(); ++ki)
+     {
+       int nreg = surfaces_[ki]->numRegions();
+       if (nreg != 1)
+	 continue;  // Not an issue currently
+       RevEngRegion *reg = surfaces_[ki]->getRegion(0);
+       if (reg->hasBlendEdge())
+	 setBlendBoundaries(reg);
+     }
+   
+#ifdef DEBUG
+   //std::cout << "Finished set blend boundaries, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions11_3" << std::endl;
+      std::ofstream of("regions11_3.g2");
+      std::ofstream ofm("mid_regions11_3.g2");
+      std::ofstream ofs("small_regions11_3.g2");
+      writeRegionStage(of, ofm, ofs);
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11_3.g2");
+	  writeRegionWithSurf(of);
+	}
+
+      vector<shared_ptr<ftEdge> > trim_edgs;
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  // if (regions_[kr]->numPoints() == 0)
+	  //   std::cout << "Finished set blend boundaries, empty region, ki=" << kr << ", region: " << regions_[kr].get() << std::endl;
+	  int num = regions_[kr]->numTrimEdges();
+	  if (num > 0)
+	    {
+	      vector<shared_ptr<ftEdge> > curr_edgs = regions_[kr]->getTrimEdges();
+	      trim_edgs.insert(trim_edgs.end(), curr_edgs.begin(), curr_edgs.end());
+	    }
+	}
+      if (trim_edgs.size() > 0)
+	{
+	  std::ofstream oft("trim_edges11_3.g2");
+	  for (size_t kr=0; kr<trim_edgs.size(); ++kr)
+	    {
+	      shared_ptr<ParamCurve> tmp = trim_edgs[kr]->geomCurve();
+	      shared_ptr<CurveOnSurface> tmp2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(tmp);
+#ifdef DEBUG_BLEND
+	      bool same_orient = tmp2->sameOrientation();
+	      bool same_trace = tmp2->sameTrace(approx_tol_);
+	      bool same_cv = tmp2->sameCurve(approx_tol_);
+	      if ((!same_orient) || (!same_trace) || (!same_cv))
+		std::cout << "Surface curve mismatch " << kr << " " << same_orient << " " << same_trace << " " << same_cv << std::endl;
+#endif
+	      shared_ptr<ParamCurve> tmp3 = tmp2->spaceCurve();
+	      tmp3->writeStandardHeader(oft);
+	      tmp3->write(oft);
+	    }
+	}
+     }
+   
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges11_3.g2");
+       writeEdgeStage(ofe);
+     }
+#endif
+
+   for (int ka=0; ka<(int)regions_.size(); ++ka)
+     {
+       if (regions_[ka]->hasSurface() && regions_[ka]->hasBlendEdge())
+   	 growBlendSurface(ka);
+     }
+   
+#ifdef DEBUG
+    std::cout << "Finished grow blend surface, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions11_3_2" << std::endl;
+      std::ofstream of("regions11_3_2.g2");
+      std::ofstream ofm("mid_regions11_3_2.g2");
+      std::ofstream ofs("small_regions11_3_2.g2");
+      writeRegionStage(of, ofm, ofs);
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11_3_2.g2");
+	  writeRegionWithSurf(of);
+	}
+
+      vector<shared_ptr<ftEdge> > trim_edgs;
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  if (regions_[kr]->numPoints() == 0)
+	    std::cout << "Finished grow blend surface, empty region, ki=" << kr << ", region: " << regions_[kr].get() << std::endl;
+	  int num = regions_[kr]->numTrimEdges();
+	  if (num > 0)
+	    {
+	      vector<shared_ptr<ftEdge> > curr_edgs = regions_[kr]->getTrimEdges();
+	      trim_edgs.insert(trim_edgs.end(), curr_edgs.begin(), curr_edgs.end());
+	    }
+	}
+      if (trim_edgs.size() > 0)
+	{
+	  std::ofstream oft("trim_edges11_3_2.g2");
+	  for (size_t kr=0; kr<trim_edgs.size(); ++kr)
+	    {
+	      shared_ptr<ParamCurve> tmp = trim_edgs[kr]->geomCurve();
+	      shared_ptr<CurveOnSurface> tmp2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(tmp);
+#ifdef DEBUG_BLEND
+	      bool same_orient = tmp2->sameOrientation();
+	      bool same_trace = tmp2->sameTrace(approx_tol_);
+	      bool same_cv = tmp2->sameCurve(approx_tol_);
+	      if ((!same_orient) || (!same_trace) || (!same_cv))
+		std::cout << "Surface curve mismatch " << kr << " " << same_orient << " " << same_trace << " " << same_cv << std::endl;
+#endif
+	      shared_ptr<ParamCurve> tmp3 = tmp2->spaceCurve();
+	      tmp3->writeStandardHeader(oft);
+	      tmp3->write(oft);
+	    }
+	}
+     }
+   
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges11_3_2.g2");
+       writeEdgeStage(ofe);
+     }
+#endif
+
+    for (int ka=0; ka<(int)regions_.size(); ++ka)
+     {
+       if (regions_[ka]->hasSurface() && regions_[ka]->numTrimEdges() > 0 &&
+	   (!regions_[ka]->hasBlendEdge()))
+    	 {
+    	   vector<vector<RevEngPoint*> > added_groups;
+    	   vector<HedgeSurface*> dummy_surfs;
+    	   double tol = 1.5*approx_tol_;
+    	   double angtol = 5.0*anglim_;
+    	   regions_[ka]->removeLowAccuracyPoints(min_point_region_, 
+    						 tol, angtol, added_groups);
+    	   if (added_groups.size() > 0)
+    	     surfaceExtractOutput(ka, added_groups, dummy_surfs);
+    	 }
+     }
+    
+    for (int ka=0; ka<(int)regions_.size(); ++ka)
+     {
+       if (regions_[ka]->hasSurface() && regions_[ka]->numTrimEdges() > 0)
+	 growMasterSurface(ka);
+     }
+   
+#ifdef DEBUG
+    std::cout << "Finished grow blend surface, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions11_3_3" << std::endl;
+      std::ofstream of("regions11_3_3.g2");
+      std::ofstream ofm("mid_regions11_3_3.g2");
+      std::ofstream ofs("small_regions11_3_3.g2");
+      writeRegionStage(of, ofm, ofs);
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11_3_3.g2");
+	  writeRegionWithSurf(of);
+	}
+
+      vector<shared_ptr<ftEdge> > trim_edgs;
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  if (regions_[kr]->numPoints() == 0)
+	    std::cout << "Grow master surface, empty region, ki=" << kr << ", region: " << regions_[kr].get() << std::endl;
+	  int num = regions_[kr]->numTrimEdges();
+	  if (num > 0)
+	    {
+	      vector<shared_ptr<ftEdge> > curr_edgs = regions_[kr]->getTrimEdges();
+	      trim_edgs.insert(trim_edgs.end(), curr_edgs.begin(), curr_edgs.end());
+	    }
+	}
+      if (trim_edgs.size() > 0)
+	{
+	  std::ofstream oft("trim_edges11_3_3.g2");
+	  for (size_t kr=0; kr<trim_edgs.size(); ++kr)
+	    {
+	      shared_ptr<ParamCurve> tmp = trim_edgs[kr]->geomCurve();
+	      shared_ptr<CurveOnSurface> tmp2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(tmp);
+#ifdef DEBUG_BLEND
+	      bool same_orient = tmp2->sameOrientation();
+	      bool same_trace = tmp2->sameTrace(approx_tol_);
+	      bool same_cv = tmp2->sameCurve(approx_tol_);
+	      if ((!same_orient) || (!same_trace) || (!same_cv))
+		std::cout << "Surface curve mismatch " << kr << " " << same_orient << " " << same_trace << " " << same_cv << std::endl;
+#endif
+	      shared_ptr<ParamCurve> tmp3 = tmp2->spaceCurve();
+	      tmp3->writeStandardHeader(oft);
+	      tmp3->write(oft);
+	    }
+	}
+     }
+   
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges11_3_3.g2");
+       writeEdgeStage(ofe);
+     }
+#endif
+
+#ifdef DEBUG_BLEND
+   std::ofstream ofbb("blend_branch.g2");
+   vector<RevEngPoint*> bbpts;
+   for (size_t kr=0; kr<regions_.size(); ++kr)
+     {
+       if (!regions_[kr]->hasBlendEdge())
+	 continue;
+       vector<RevEngPoint*> currbb = regions_[kr]->extractBranchPoints();
+       if (currbb.size() > 0)
+	 bbpts.insert(bbpts.end(), currbb.begin(), currbb.end());
+     }
+   if (bbpts.size() > 0)
+     {
+       ofbb << "400 1 0 4 0 0 0 255" << std::endl;
+       ofbb << bbpts.size() << std::endl;
+       for (size_t kr=0; kr<bbpts.size(); ++kr)
+	 ofbb << bbpts[kr]->getPoint() << std::endl;
+     }
+#endif
+
+  // TESTING. Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+#ifdef DEBUG
+   std::cout << "Torus corners" << std::endl;
+#endif
+
+   for (size_t ki=0; ki<regions_.size(); ++ki)
+     {
+       if (regions_[ki]->toBeRemoved())
+	 continue;
+       
+      if (!regions_[ki]->hasSurface())
+	continue;
+
+      if (!regions_[ki]->hasBlendEdge())
+	continue;
+
+      bool done = defineTorusCorner(ki);
+#ifdef DEBUG_BLEND
+      std::ofstream ofsfs("curr_sfs.g2");
+      for (size_t kj=0; kj<regions_.size(); ++kj)
+	{
+	  if (regions_[kj]->hasSurface())
+	    {
+	      shared_ptr<ParamSurface> curr_sf = regions_[kj]->getSurface(0)->surface();
+	      curr_sf->writeStandardHeader(ofsfs);
+	      curr_sf->write(ofsfs);
+	    }
+	}
+      int stop_tor = 1;
+#endif
+     }
+   vector<RevEngRegion*> removereg;
+   vector<HedgeSurface*> removehedge;
+   for (size_t ki=0; ki<regions_.size(); ++ki)
+     {
+       if (regions_[ki]->toBeRemoved())
+	 {
+	   removereg.push_back(regions_[ki].get());
+	   int num_sfs = regions_[ki]->numSurface();
+	   for (int ka=0; ka<num_sfs; ++ka)
+	     removehedge.push_back(regions_[ki]->getSurface(ka));
+	 }
+     }
+   if (removereg.size() > 0)
+     {
+      int dummy_ix = 0;
+      updateRegionsAndSurfaces(dummy_ix, removereg, removehedge);
+     }
+   
+#ifdef DEBUG
+    std::cout << "Finished torus corners, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions11_4" << std::endl;
+      std::ofstream of("regions11_4.g2");
+      std::ofstream ofm("mid_regions11_4.g2");
+      std::ofstream ofs("small_regions11_4.g2");
+      writeRegionStage(of, ofm, ofs);
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11_4.g2");
+	  writeRegionWithSurf(of);
+	}
+      
+      vector<shared_ptr<ftEdge> > trim_edgs;
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  // if (regions_[kr]->numPoints() == 0)
+	  //   std::cout << "Torus corner, ki=" << kr << ", region: " << regions_[kr].get() << std::endl;
+	  int num = regions_[kr]->numTrimEdges();
+	  if (num > 0)
+	    {
+	      vector<shared_ptr<ftEdge> > curr_edgs = regions_[kr]->getTrimEdges();
+	      trim_edgs.insert(trim_edgs.end(), curr_edgs.begin(), curr_edgs.end());
+	    }
+	}
+      if (trim_edgs.size() > 0)
+	{
+	  std::ofstream oft("trim_edges11_4.g2");
+	  for (size_t kr=0; kr<trim_edgs.size(); ++kr)
+	    {
+	      shared_ptr<ParamCurve> tmp = trim_edgs[kr]->geomCurve();
+	      shared_ptr<CurveOnSurface> tmp2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(tmp);
+#ifdef DEBUG_BLEND
+	      bool same_orient = tmp2->sameOrientation();
+	      bool same_trace = tmp2->sameTrace(approx_tol_);
+	      bool same_cv = tmp2->sameCurve(approx_tol_);
+	      if ((!same_orient) || (!same_trace) || (!same_cv))
+		std::cout << "Surface curve mismatch " << kr << " " << same_orient << " " << same_trace << " " << same_cv << std::endl;
+#endif
+	      shared_ptr<ParamCurve> tmp3 = tmp2->spaceCurve();
+	      tmp3->writeStandardHeader(oft);
+	      tmp3->write(oft);
+	    }
+	}
+    }
+#endif
+   // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  vector<RevEngRegion*> cand_corner_adj;
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      if (!regions_[ki]->hasBlendEdge())
+	continue;
+      if (regions_[ki]->getBlendEdge()->isClosed(approx_tol_))
+	continue;
+      int num_edg = regions_[ki]->numTrimEdges();
+      if (num_edg < 4)
+	cand_corner_adj.push_back(regions_[ki].get());
+    }
+
+#ifdef DEBUG_BLEND
+  std::ofstream ofmb0("adj_candidate_blend_corner.g2");
+  for (size_t ki=0; ki<cand_corner_adj.size(); ++ki)
+    {
+      cand_corner_adj[ki]->writeRegionPoints(ofmb0);
+      cand_corner_adj[ki]->writeSurface(ofmb0);
+    }
+#endif
+  if (cand_corner_adj.size() > 2)
+    defineMissingCorner(cand_corner_adj);
+   
+   removereg.clear();
+   removehedge.clear();
+   for (size_t ki=0; ki<regions_.size(); ++ki)
+     {
+       if (regions_[ki]->toBeRemoved())
+	 {
+	   removereg.push_back(regions_[ki].get());
+	   int num_sfs = regions_[ki]->numSurface();
+	   for (int ka=0; ka<num_sfs; ++ka)
+	     removehedge.push_back(regions_[ki]->getSurface(ka));
+	 }
+     }
+   if (removereg.size() > 0)
+     {
+      int dummy_ix = 0;
+      updateRegionsAndSurfaces(dummy_ix, removereg, removehedge);
+     }
+
+#ifdef DEBUG
+    std::cout << "Finished missing corners, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions11_5" << std::endl;
+      std::ofstream of("regions11_5.g2");
+      std::ofstream ofm("mid_regions11_5.g2");
+      std::ofstream ofs("small_regions11_5.g2");
+      writeRegionStage(of, ofm, ofs);
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11_5.g2");
+	  writeRegionWithSurf(of);
+	}
+      
+      vector<shared_ptr<ftEdge> > trim_edgs;
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  // if (regions_[kr]->numPoints() == 0)
+	  //   std::cout << "Missing corner, ki=" << kr << ", region: " << regions_[kr].get() << std::endl;
+	  int num = regions_[kr]->numTrimEdges();
+	  if (num > 0)
+	    {
+	      vector<shared_ptr<ftEdge> > curr_edgs = regions_[kr]->getTrimEdges();
+	      trim_edgs.insert(trim_edgs.end(), curr_edgs.begin(), curr_edgs.end());
+	    }
+	}
+      if (trim_edgs.size() > 0)
+	{
+	  std::ofstream oft("trim_edges11_5.g2");
+	  for (size_t kr=0; kr<trim_edgs.size(); ++kr)
+	    {
+	      shared_ptr<ParamCurve> tmp = trim_edgs[kr]->geomCurve();
+	      shared_ptr<CurveOnSurface> tmp2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(tmp);
+#ifdef DEBUG_BLEND
+	      bool same_orient = tmp2->sameOrientation();
+	      bool same_trace = tmp2->sameTrace(approx_tol_);
+	      bool same_cv = tmp2->sameCurve(approx_tol_);
+	      if ((!same_orient) || (!same_trace) || (!same_cv))
+		std::cout << "Surface curve mismatch " << kr << " " << same_orient << " " << same_trace << " " << same_cv << std::endl;
+#endif
+	      shared_ptr<ParamCurve> tmp3 = tmp2->spaceCurve();
+	      tmp3->writeStandardHeader(oft);
+	      tmp3->write(oft);
+	    }
+	}
+    }
+#endif
+      
+  int stop_break_blend = 1;
+ 
+}
+
+
+//===========================================================================
+void RevEng::equalizeBlendRadii()
+//===========================================================================
+{
+  // Equialize adjacent blends between the same surfaces
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      if (!regions_[ki]->hasSurface())
+	continue;
+      
+      if (!regions_[ki]->hasBlendEdge())
+	continue;
+
+      RevEngEdge *edge1 = regions_[ki]->getBlendEdge();
+      RevEngRegion* adj1[2];
+      edge1->getAdjacent(adj1[0], adj1[1]);
+
+      
+      for (size_t kj=ki+1; kj<regions_.size(); ++kj)
+	{
+	  if (!regions_[kj]->hasSurface())
+	    continue;
+      
+	  if (!regions_[kj]->hasBlendEdge())
+	    continue;
+	  
+	  RevEngEdge *edge2 = regions_[kj]->getBlendEdge();
+	  RevEngRegion* adj2[2];
+	  edge2->getAdjacent(adj2[0], adj2[1]);
+
+	  if ((adj1[0] == adj2[0] || adj1[0] == adj2[1]) &&
+	      (adj1[1] == adj2[0] || adj1[1] == adj2[1]))
+	    {
+#ifdef DEBUG_BLEND
+	      std::ofstream of("adj_blend.g2");
+	      regions_[ki]->writeRegionPoints(of);
+	      regions_[kj]->writeRegionPoints(of);
+#endif
+	      double par1, par2;
+	      bool is_adjacent = edge1->isAdjacent(edge2, approx_tol_, par1,
+						   par2);
+	      if (is_adjacent)
+		equalizeAdjacent(ki, kj);
+	    }
+	}
+  
+    }
+  
+  // Collect information about blend radii
+  vector<double> rad;
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      if (!regions_[ki]->hasSurface())
+	continue;
+      
+      if (!regions_[ki]->hasBlendEdge())
+	continue;
+
+      shared_ptr<ParamSurface> surf = regions_[ki]->getSurface(0)->surface();
+      shared_ptr<ElementarySurface> elem =
+	dynamic_pointer_cast<ElementarySurface, ParamSurface>(surf);
+      if (!elem.get())
+	continue;
+
+      double radius = elem->radius(0.0, 0.0);
+      double radius2 = elem->radius2(0.0, 0.0);
+      if (radius2 > 0)
+	rad.push_back(radius2);
+      else
+	rad.push_back(radius);
+    }
+
+  if (rad.size() == 0)
+    return;
+  
+  std::sort(rad.begin(), rad.end());
+  vector<size_t> ixs;
+  ixs.push_back(0);
+  double tmean = rad[0];
+  size_t prev = 0;
+  int nn = 1;
+  size_t ki=1;
+  double fac = 0.5;
+  double curr_mean, range;
+  for (; ki<rad.size(); ++ki)
+    {
+      curr_mean = (tmean + rad[ki])/(double)(nn+1);
+      range = rad[ki]-rad[prev];
+      if (range < fac*curr_mean)
+	{
+	  tmean += rad[ki];
+	  ++nn;
+	}
+      else
+	{
+	  ixs.push_back(ki);
+	  prev = ki;
+	  tmean = rad[ki];
+	  nn = 1;
+	}
+      int stop_break0 = 1;
+    }
+
+  vector<pair<double,double> > rad_range;
+  for (ki=1; ki<ixs.size(); ++ki)
+    rad_range.push_back(std::make_pair(rad[ixs[ki-1]], rad[ixs[ki]-1]));
+  rad_range.push_back(std::make_pair(rad[ixs[ixs.size()-1]], rad[rad.size()-1]));
+
+  vector<double> mean_rad(rad_range.size());
+  for (ki=0; ki<rad_range.size(); ++ki)
+    {
+      mean_rad[ki] = 0.5*(rad_range[ki].first + rad_range[ki].second);
+      if (mean_rad[ki] <= 0.1)
+	{
+	  double low = 0.01*(double)((int)(100.0*mean_rad[ki]));
+	  double high = 0.01*(double)((int)(100.0*mean_rad[ki]+1));
+	  mean_rad[ki] = (mean_rad[ki]-low < high-mean_rad[ki]) ? low : high;
+	}
+      else if (mean_rad[ki] <= 1.0)
+	{
+	  double low = 0.1*(double)((int)(10.0*mean_rad[ki]));
+	  double high = 0.1*(double)((int)(10.0*mean_rad[ki]+1));
+	  mean_rad[ki] = (mean_rad[ki]-low < high-mean_rad[ki]) ? low : high;
+	}
+      else if (mean_rad[ki] <= 10.0)
+	{
+	  double low = (double)((int)(mean_rad[ki]));
+	  double high = (double)((int)(mean_rad[ki]+1));
+	  double mid = 0.5*(low+high);
+	  mean_rad[ki] = (mean_rad[ki]-low < std::min(fabs(mean_rad[ki]-mid), high-mean_rad[ki])) ? 
+	    low : ((high-mean_rad[ki]) < fabs(mean_rad[ki]-mid) ? high : mid);;
+	}
+      else
+	{
+	  double low = (double)((int)(mean_rad[ki]));
+	  double high = (double)((int)(mean_rad[ki]+1));
+	  mean_rad[ki] = (mean_rad[ki]-low < high-mean_rad[ki]) ? low : high;
+	}
+    }
+  
+  for (ki=0; ki<regions_.size(); ++ki)
+    {
+      if (!regions_[ki]->hasSurface())
+	continue;
+      
+      if (!regions_[ki]->hasBlendEdge())
+	continue;
+
+      shared_ptr<ParamSurface> surf = regions_[ki]->getSurface(0)->surface();
+      shared_ptr<ElementarySurface> elem =
+	dynamic_pointer_cast<ElementarySurface, ParamSurface>(surf);
+      if (!elem.get())
+	continue;
+
+      double radius = elem->radius(0.0, 0.0);
+      double radius2 = elem->radius2(0.0, 0.0);
+      if (radius2 > 0.0)
+	radius = radius2;
+
+      size_t kj;
+      for (kj=0; kj<rad_range.size(); ++kj)
+	if (radius >= rad_range[kj].first && radius <= rad_range[kj].second)
+	  {
+	    updateBlendRadius(ki, mean_rad[kj]);
+	    break;
+	  }
+    }
+
+    int stop_break = 1;
+
+}
+
+//===========================================================================
+void RevEng::equalizeAdjacent(size_t ix1, size_t ix2)
+//===========================================================================
+{
+  shared_ptr<ParamSurface> surf1 = regions_[ix1]->getSurface(0)->surface();
+  shared_ptr<ParamSurface> surf2 = regions_[ix2]->getSurface(0)->surface();
+  if ((!surf1) || (!surf2))
+    return;
+
+  if (surf1->instanceType() != surf2->instanceType())
+    return;
+
+  double angtol = 5.0*anglim_;
+  shared_ptr<ElementarySurface> elem1 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+  shared_ptr<ElementarySurface> elem2 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+  Point axis1 = elem1->direction();
+  Point axis2 = elem1->direction();
+  double ang = axis1.angle(axis2);
+  ang = std::min(ang, M_PI-ang);
+  if (ang > angtol)
+    return;
+  Point pos1 = elem1->location();
+  Point pos2 = elem2->location();
+  double rad1 = elem1->radius(0.0, 0.0);
+  double rad2 = elem2->radius(0.0, 0.0);
+  double fac = 0.1;
+  double fac2 = 0.25;
+  if (fabs(rad1-rad2) > fac*std::max(rad1, rad2))
+    return;  // Too large difference
+  double rad = 0.5*(rad1 + rad2);
+  
+  if (axis1*axis2 < 0.0)
+    axis2 *= -1;
+  Point axis = 0.5*(axis1 + axis2);
+  axis.normalize();
+
+  Point Cx;
+  Point Cx1 = elem1->direction2();
+  Point Cx2 = elem2->direction2();
+  double ang2 = Cx1.angle(Cx2);
+  if (ang2 <= angtol || M_PI-ang2 <= angtol)
+    {
+      if (Cx1*Cx2 < 0.0)
+	Cx2 *= -1;
+      Cx = 0.5*(Cx1 + Cx2);
+      Point Cy = axis.cross(Cx);
+      Cx = Cy.cross(axis);
+      Cx.normalize();
+    }
+  else
+    {
+      int ka = -1;
+      double minang = std::numeric_limits<double>::max();
+      for (int kb=0; kb<3; ++kb)
+	{
+	  double ang3 = mainaxis_[kb].angle(axis);
+	  ang3 = std::min(ang3, M_PI-ang3);
+	  if (ang3 < minang)
+	    {
+	      minang = ang3;
+	      ka = kb;
+	    }
+	}
+      if (ka < 0)
+	return;
+      int kb = (ka > 0) ? ka - 1 : 2;
+      Cx = mainaxis_[kb].cross(axis);
+    }
+
+  RectDomain dom1 = elem1->getParameterBounds();
+  RectDomain dom2 = elem2->getParameterBounds();
+  shared_ptr<ElementarySurface> upd1, upd2;
+  bool cyllike = true;
+  if (surf1->instanceType() == Class_Cylinder)
+    {
+      Point pos2_2 = pos1 + ((pos2 - pos1)*axis)*axis;
+      if (pos2.dist(pos2_2) > approx_tol_)
+	return;
+
+     Point pos1_2 = pos2 + ((pos1 - pos2)*axis)*axis;
+      if (pos1.dist(pos1_2) > approx_tol_)
+	return;
+
+      Point pos3 = 0.5*(pos1_2 + pos2_2);  // Point on updated axis
+      Point pos3_1 = pos3 + ((pos1 - pos3)*axis)*axis;
+      Point pos3_2 = pos3 + ((pos2 - pos3)*axis)*axis;
+
+      double rad = 0.5*(rad1 + rad2);
+      upd1 = shared_ptr<Cylinder>(new Cylinder(rad, pos3_1, axis, Cx));
+      upd2 = shared_ptr<Cylinder>(new Cylinder(rad, pos3_2, axis, Cx));
+
+      double upar1 = 0.5*(dom1.umin() + dom2.umin());  // Could be a problem if
+      // the direction of Cx is changed significantly
+      double upar2 = 0.5*(dom1.umax() + dom2.umax());
+      upd1->setParameterBounds(upar1, dom1.vmin(), upar2, dom1.vmax());
+      upd2->setParameterBounds(upar1, dom2.vmin(), upar2, dom2.vmax());
+      cyllike = true;
+    }
+  else if (surf1->instanceType() == Class_Torus)
+    {
+      Point pos2_2 = pos1 + ((pos2 - pos1)*axis)*axis;
+      if (pos2_2.dist(pos2) > approx_tol_)
+	return;
+
+      double minrad1 = elem1->radius2(0.0, 0.0);
+      double minrad2 = elem2->radius2(0.0, 0.0);
+      if (fabs(minrad1-minrad2) > fac2*std::max(minrad1, minrad2))
+	return;
+
+      Point centre = 0.5*(pos1 + pos2);
+      double minrad = 0.5*(minrad1 + minrad2);
+      upd1 = shared_ptr<Torus>(new Torus(rad, minrad, centre, axis, Cx));
+      upd2 = shared_ptr<Torus>(new Torus(rad, minrad, centre, axis, Cx));
+      double vpar1 = 0.5*(dom1.vmin() + dom2.vmin());
+      double vpar2 = 0.5*(dom1.vmax() + dom2.vmax());
+      upd1->setParameterBounds(dom1.umin(), vpar1, dom1.umax(), vpar2);
+      upd2->setParameterBounds(dom2.umin(), vpar1, dom2.umax(), vpar2);
+      cyllike = false;
+    }
+  else
+    return;  // Not supported
+#ifdef DEBUG_BLEND
+  std::ofstream of("updated_adjacent.g2");
+  upd1->writeStandardHeader(of);
+  upd1->write(of);
+  upd2->writeStandardHeader(of);
+  upd2->write(of);
+#endif
+
+  // Parameterize points
+  double maxd1, avd1;
+  int nmb_in1, nmb2_in1;
+  vector<RevEngPoint*> in1, out1;
+  vector<pair<double,double> > dist_ang1;
+  vector<double> parvals1;
+  RevEngUtils::distToSurf(regions_[ix1]->pointsBegin(), regions_[ix1]->pointsEnd(),
+			  upd1, approx_tol_, maxd1, avd1, nmb_in1, nmb2_in1, in1, out1,
+			  parvals1, dist_ang1, angtol);
+  int sf_flag1 = regions_[ix1]->defineSfFlag(0, approx_tol_, nmb_in1, nmb2_in1, avd1,
+					     cyllike);
+  int num_pts1 = regions_[ix1]->numPoints();
+  for (int ka=0; ka<num_pts1; ++ka)
+    {
+      RevEngPoint *curr = regions_[ix1]->getPoint(ka);
+      curr->setPar(Vector2D(parvals1[2*ka],parvals1[2*ka+1]));
+      curr->setSurfaceDist(dist_ang1[ka].first, dist_ang1[ka].second);
+    }
+  regions_[ix1]->updateInfo(approx_tol_, angtol);
+  regions_[ix1]->setSurfaceFlag(sf_flag1);
+
+  // Replace Surface
+  regions_[ix1]->getSurface(0)->replaceSurf(upd1);
+  
+  double maxd2, avd2;
+  int nmb_in2, nmb2_in2;
+  vector<RevEngPoint*> in2, out2;
+  vector<pair<double,double> > dist_ang2;
+  vector<double> parvals2;
+  RevEngUtils::distToSurf(regions_[ix2]->pointsBegin(), regions_[ix2]->pointsEnd(),
+			  upd1, approx_tol_, maxd2, avd2, nmb_in2, nmb2_in2, in2, out2,
+			  parvals2, dist_ang2, angtol);
+  int sf_flag2 = regions_[ix2]->defineSfFlag(0, approx_tol_, nmb_in2, nmb2_in2, avd2,
+					     cyllike);
+  int num_pts2 = regions_[ix2]->numPoints();
+  for (int ka=0; ka<num_pts2; ++ka)
+    {
+      RevEngPoint *curr = regions_[ix2]->getPoint(ka);
+      curr->setPar(Vector2D(parvals2[2*ka],parvals2[2*ka+1]));
+      curr->setSurfaceDist(dist_ang2[ka].first, dist_ang2[ka].second);
+    }
+  regions_[ix2]->updateInfo(approx_tol_, angtol);
+  regions_[ix2]->setSurfaceFlag(sf_flag2);
+
+  // Replace Surface
+  regions_[ix2]->getSurface(0)->replaceSurf(upd2);
+  
+}
+
+//===========================================================================
+void RevEng::updateBlendRadius(size_t ix, double radius)
+//===========================================================================
+{
+  double angtol = 5.0*anglim_;
+  double eps = 1.0e-6;
+  RevEngEdge *edge = regions_[ix]->getBlendEdge();
+  RevEngRegion* adj[2];
+  edge->getAdjacent(adj[0], adj[1]);
+
+  if ((!adj[0]->hasSurface()) || (!adj[1]->hasSurface()))
+    return;  // Something wrong
+
+  bool out1 = false, out2 = false;
+  edge->getOuterInfo(out1, out2);
+
+  // Intersection curve
+  vector<shared_ptr<CurveOnSurface> > cvs;
+  edge->getCurve(cvs);
+  vector<Point> der(2);
+  cvs[0]->point(der, 0.5*(cvs[0]->startparam()+cvs[0]->endparam()), 1);
+
+  shared_ptr<ParamSurface> surf = regions_[ix]->getSurface(0)->surface();
+  shared_ptr<Cylinder> init_cyl =
+    dynamic_pointer_cast<Cylinder, ParamSurface>(surf);
+  shared_ptr<Torus> init_tor =
+    dynamic_pointer_cast<Torus, ParamSurface>(surf);
+  if ((!init_cyl.get()) && (!init_tor.get()))
+    return;
+  shared_ptr<ParamSurface> surf1 = adj[0]->getSurface(0)->surface();
+  shared_ptr<ParamSurface> surf2 = adj[1]->getSurface(0)->surface();
+  shared_ptr<ElementarySurface> elem1 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+  shared_ptr<ElementarySurface> elem2 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+  shared_ptr<ElementarySurface> upd_blend;
+  double ylen;
+  double init_rad;
+  Point dir1 = elem1->direction();
+  Point norm1 = adj[0]->getMeanNormalTriang();
+  if (elem1->instanceType() == Class_Plane && dir1*norm1 < 0.0)
+    dir1 *= -1;
+  Point dir2 = elem2->direction();
+  Point norm2 = adj[1]->getMeanNormalTriang();
+  if (elem2->instanceType() == Class_Plane && dir2*norm2 < 0.0)
+    dir2 *= -1;
+  if (init_cyl.get())
+    {
+      if (elem1->instanceType() != Class_Plane || elem2->instanceType() != Class_Plane)
+	return; 
+
+      Point lin1, lin2;
+      Point dir1_2 = dir1, dir2_2 = dir2;
+      if (elem1->instanceType() == Class_Plane)
+	lin1 = der[1].cross(dir1);
+      else
+	{
+	  double clo_u, clo_v, clo_dist;
+	  Point clo;
+	  surf1->closestPoint(der[0], clo_u, clo_v, clo, clo_dist, eps);
+	  surf1->normal(dir1_2, clo_u, clo_v);
+	  lin1 = der[1].cross(dir1_2);
+	}
+      
+      if (elem2->instanceType() == Class_Plane)
+	lin2 = der[1].cross(dir2);
+      else
+	{
+	  double clo_u, clo_v, clo_dist;
+	  Point clo;
+	  surf2->closestPoint(der[0], clo_u, clo_v, clo, clo_dist, eps);
+	  surf2->normal(dir2_2, clo_u, clo_v);
+	  lin2 = der[1].cross(dir2_2);
+	}
+     
+      // Create new cylinder
+      Point axis = init_cyl->direction();
+      Point Cx = init_cyl->direction2();
+      init_rad = init_cyl->getRadius();
+      lin1.normalize();
+      if (lin1*dir2_2 < 0.0)
+	lin1 *= -1;
+      lin2.normalize();
+      if (lin2*dir1_2 < 0.0)
+	lin2 *= -1;
+
+      Point vec = lin1 + lin2;
+      vec.normalize();
+      double alpha = lin1.angle(lin2);
+      double fac = 1.0/sin(0.5*alpha);
+      int sgn = (out1 && out2) ? 1 : -1;
+      Point loc = der[0] + sgn*fac*radius*vec;
+      double xlen = der[0].dist(loc);
+      ylen = sqrt(xlen*xlen - radius*radius);
+
+      upd_blend = shared_ptr<Cylinder>(new Cylinder(radius, loc, axis, Cx));
+    }
+  else if (init_tor.get())
+    {
+      // int plane_ix = 0;
+      // if (elem2->instanceType() == Class_Plane)
+      // 	{
+      // 	  std::swap(elem1, elem2);
+      // 	  plane_ix = 1;
+      // 	}
+      // if (elem1->instanceType() != Class_Plane)
+      // 	return; 
+      // if (elem2->instanceType() != Class_Cylinder &&
+      // 	  elem2->instanceType() != Class_Cone)
+      // 	return;
+      
+      // init_rad = init_tor->radius2(0.0, 0.0);
+
+      // bool rot_out = (plane_ix == 0) ? out2 : out1;
+      // bool plane_out = (plane_ix == 0) ? out1 : out2;
+      // int sgn1 = plane_out ? -1 : 1;
+      // int sgn2 = rot_out ? -1 : 1;
+      // Point normal0 = elem1->direction();
+      // Point norm1 = adj[plane_ix]->getMeanNormalTriang();
+      // if (normal0*norm1 < 0.0)
+      // 	sgn1 *= -1;
+
+      bool plane1 = (elem1->instanceType() == Class_Plane);
+      bool plane2 = (elem2->instanceType() == Class_Plane);
+      shared_ptr<Cone> cone1 = dynamic_pointer_cast<Cone,ElementarySurface>(elem1);
+      shared_ptr<Cone> cone2 = dynamic_pointer_cast<Cone,ElementarySurface>(elem2);
+      shared_ptr<ElementarySurface> rotational;
+      if (plane1)
+	rotational = elem2;
+      else if (plane2)
+	rotational = elem1;
+      else if (cone1.get())
+	rotational = elem2;  // elem2 is expected to be a cylinder
+      else if (cone2.get())
+	rotational = elem1;
+      double alpha1 = 0.0, alpha2 = 0.0;
+      if (cone1.get())
+	alpha1 = cone1->getConeAngle();
+      if (cone2.get())
+	alpha2 = cone2->getConeAngle();
+      double alpha = fabs(alpha1) + fabs(alpha2);
+      double beta = (plane1 || plane2) ? 0.5*M_PI + alpha : M_PI-fabs(alpha);
+      double phi2 = 0.5*(M_PI - beta);
+      Point axis = rotational->direction();
+      Point loc = rotational->location();
+      double rd = (der[0]-loc)*axis;
+      Point centr = loc + rd*axis;
+      double hh = init_tor->location().dist(centr);
+      init_rad = init_tor->radius2(0.0, 0.0);
+      double d2 = hh/sin(phi2) - init_rad;
+      int sgn = 1;
+      if ((elem1->instanceType() == Class_Plane && elem1->direction()*dir1 < 0.0) ||
+	  (elem2->instanceType() == Class_Plane && elem2->direction()*dir2 < 0.0))
+	sgn = -1;
+      double Rrad;
+      Point centre, normal, Cx;
+      bool OK = getTorusParameters(elem1, elem2, der[0], radius, d2, out1, out2, 
+				   sgn, Rrad, centre, normal, Cx);
+      if (!OK)
+	return;  // Don't change
+     
+      upd_blend = shared_ptr<Torus>(new Torus(Rrad, radius, centre, normal, Cx));
+
+      // bool setbounds = false;
+      // if ((plane1 && out2) || (plane2 && out1) ||
+      // 	  (plane1 && false && plane2 == false &&
+      // 	   ((cone1.get() && out1 == false) || (cone2.get() && out2 == false))))
+      // 	setbounds = true;
+	
+      // if (setbounds)
+      // 	{
+	  RectDomain dom = init_tor->getParameterBounds();
+	  upd_blend->setParameterBounds(dom.umin(), dom.vmin()-M_PI,
+					dom.umax(), dom.vmax()-M_PI);
+	// }
+      double xlen = der[0].dist(centre);
+      ylen = fabs(xlen - Rrad);
+     }
+  
+  // RectDomain dom = init_cyl->getParameterBounds();
+  // cyl->setParameterBounds(dom.umin(), dom.vmin(), dom.umax(), dom.vmax());
+#ifdef DEBUG_BLEND
+  std::ofstream of("blend2.g2");
+  regions_[ix]->writeRegionPoints(of);
+  surf->writeStandardHeader(of);
+  surf->write(of);
+
+  adj[0]->writeRegionPoints(of);
+  adj[1]->writeRegionPoints(of);
+  upd_blend->writeStandardHeader(of);
+  upd_blend->write(of);
+  RectDomain dom2 = upd_blend->getParameterBounds();
+  vector<shared_ptr<ParamCurve> > tmp_cvs = upd_blend->constParamCurves(dom2.vmin(), true);
+  tmp_cvs[0]->writeStandardHeader(of);
+  tmp_cvs[0]->write(of);
+#endif
+
+  if (radius < init_rad)
+    {
+      // Move outside points from the blend surface to the adjacent
+      // surfaces. If the new radius is larger than the initial one,
+      // moving of points will be performed by the next function
+      vector<RevEngPoint*> points(regions_[ix]->pointsBegin(), regions_[ix]->pointsEnd());
+      vector<vector<RevEngPoint*> > out_pts(2);
+      adj[0]->sortBlendPoints(points, cvs, ylen, adj[1], out_pts[0], out_pts[1]);
+      vector<RevEngPoint*> all_out;
+      if (out_pts[0].size() > 0)
+	all_out.insert(all_out.end(), out_pts[0].begin(), out_pts[0].end());
+      if (out_pts[1].size() > 0)
+	all_out.insert(all_out.end(), out_pts[1].begin(), out_pts[1].end());
+      if (all_out.size() > 0)
+	regions_[ix]->removePoints(all_out);
+      
+      for (int ka=0; ka<2; ++ka)
+	{
+	  vector<RevEngPoint*> to_blend;  // Not here
+	  adj[ka]->updateWithPointsInOut(to_blend, out_pts[ka], approx_tol_, angtol);
+	}
+    }
+
+  // Parameterize remaining points
+  double maxd, avd;
+  int nmb_in, nmb2_in;
+  vector<RevEngPoint*> in, out;
+  vector<pair<double,double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(regions_[ix]->pointsBegin(), regions_[ix]->pointsEnd(),
+			  upd_blend, approx_tol_, maxd, avd, nmb_in, nmb2_in, in, out,
+			  parvals, dist_ang, angtol);
+  int sf_flag = regions_[ix]->defineSfFlag(0, approx_tol_, nmb_in, nmb2_in, avd, true);
+  int num_pts = regions_[ix]->numPoints();
+  for (int ka=0; ka<num_pts; ++ka)
+    {
+      RevEngPoint *curr = regions_[ix]->getPoint(ka);
+      curr->setPar(Vector2D(parvals[2*ka],parvals[2*ka+1]));
+      curr->setSurfaceDist(dist_ang[ka].first, dist_ang[ka].second);
+    }
+  regions_[ix]->setSurfaceFlag(sf_flag);
+
+  // Replace Surface
+  regions_[ix]->getSurface(0)->replaceSurf(upd_blend);
+}
+
+
+//===========================================================================
+bool RevEng::defineTorusCorner(size_t ix)
+//===========================================================================
+{
+  double pihalf = 0.5*M_PI;
+  double angtol = 5.0*anglim_;
+  double blendfac = 2.0;
+  
+  shared_ptr<ParamSurface> surf1 = regions_[ix]->getSurface(0)->surface();
+  if (surf1->instanceType() != Class_Cylinder)
+    return false;
+
+  if (!regions_[ix]->hasBlendEdge())
+    return false;
+
+  // Bound cylinder in the length direction
+  double diag = bbox_.low().dist(bbox_.high());
+  if (!surf1->isBounded())
+    {
+      shared_ptr<Cylinder> cyl = dynamic_pointer_cast<Cylinder,ParamSurface>(surf1);
+      cyl->setParamBoundsV(-0.5*diag, 0.5*diag);
+    }
+
+  RevEngEdge* blend_edge = regions_[ix]->getBlendEdge();
+  RevEngRegion *adj1=0, *adj2=0;
+  blend_edge->getAdjacent(adj1, adj2);
+  
+  vector<RevEngEdge*> revedg1 = regions_[ix]->getAllRevEdges();
+  shared_ptr<ElementarySurface> elem1 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+  Point dir1 = elem1->direction();
+
+#ifdef DEBUG_BLEND
+  std::ofstream of0("pot_tor_adj.g2");
+  surf1->writeStandardHeader(of0);
+  surf1->write(of0);
+#endif  
+  // Looking for a plane
+  bool found_torus = false;
+  for (size_t kj=0; kj<regions_.size(); ++kj)
+    {
+      if (kj == ix)
+	continue;
+      if (regions_[kj]->toBeRemoved())
+	continue;
+      if (!regions_[kj]->hasSurface())
+	continue;
+      if (regions_[kj].get() == adj1 || regions_[kj].get() == adj2)
+	continue;
+
+      shared_ptr<ParamSurface> surf2 = regions_[kj]->getSurface(0)->surface();
+      if (surf2->instanceType() != Class_Plane)
+	continue;
+
+      if (!(regions_[ix]->isAdjacent(regions_[kj].get())
+	    || regions_[ix]->isNextToAdjacent(regions_[kj].get())))
+	continue;
+
+      // Check if the plane and the cylinder already has a common RevEngEdge
+      vector<RevEngEdge*> revedg2 = regions_[kj]->getAllRevEdges();
+      size_t kr, kh;
+      for (kr=0; kr<revedg1.size(); ++kr)
+	{
+	  for (kh=0; kh<revedg2.size(); ++kh)
+	    if (revedg1[kr] == revedg2[kh])
+	      break;
+	  if (kh < revedg2.size())
+	    break;
+	}
+      if (kr < revedg1.size())
+	continue;
+      
+#ifdef DEBUG_BLEND
+      std::ofstream of1("torus_blend1.g2");
+      regions_[ix]->writeRegionPoints(of1);
+      regions_[ix]->writeSurface(of1);
+      regions_[kj]->writeRegionPoints(of1);
+      regions_[kj]->writeSurface(of1);
+#endif
+      
+      shared_ptr<ElementarySurface> elem2 =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+      if (!elem2->isBounded())
+	elem2->setParameterBounds(-0.5*diag, -0.5*diag, 0.5*diag, 0.5*diag);
+      Point dir2 = elem2->direction();
+      double ang = dir1.angle(dir2);
+      ang = fabs(pihalf - ang);
+      if (ang > blendfac*angtol)
+	{
+	  double usz, vsz;
+	  elem1->estimateSfSize(usz, vsz);
+	  double lenlim = 0.9*usz;
+
+	  vector<shared_ptr<RevEngEdge> > edges =
+	    defineEdgesBetween(ix, elem1, dir1, kj, elem2, dir2, false,
+			       lenlim, false);
+	  if (edges.size() > 0)
+	    {
+	      size_t ix2 = edges_.size();
+	      edges_.insert(edges_.end(), edges.begin(), edges.end());
+	      bool found = createTorusBlend(ix2);
+	      // if (!found)
+	      // 	found = createBlendSurface((int)ix2);
+	      if (found)
+		found_torus = true;
+	      else
+		edges_.erase(edges_.begin()+ix2, edges_.end());
+	    }
+	}
+    }
+
+  return found_torus;
+}
+
+//===========================================================================
+void RevEng::defineMissingCorner(vector<RevEngRegion*>& cand_adj)
+//===========================================================================
+{
+  double pihalf = 0.5*M_PI;
+  double angtol = 5.0*anglim_;
+  double diag = bbox_.low().dist(bbox_.high());
+  
+  // Get candidate opposite regions
+  vector<RevEngRegion*> opposite_reg;
+  for (size_t ki=0; ki<cand_adj.size(); ++ki)
+    {
+      RevEngEdge* edg1 = cand_adj[ki]->getBlendEdge();
+      if (!edg1)
+	continue;
+      RevEngRegion *adj1, *adj2;
+      edg1->getAdjacent(adj1, adj2);
+      if ((!adj1) || (!adj2))
+	continue;
+      for (size_t kj=ki+1; kj<cand_adj.size(); ++kj)
+	{
+	  RevEngEdge* edg2 = cand_adj[kj]->getBlendEdge();
+	  if (!edg2)
+	    continue;
+	  RevEngRegion *adj3, *adj4;
+	  edg2->getAdjacent(adj3, adj4);
+	  if ((!adj3) || (!adj4))
+	    continue;
+	  if (adj1 == adj3 || adj1 == adj4)
+	    opposite_reg.push_back(adj1);
+	  if (adj2 == adj3 || adj2 == adj4)
+	    opposite_reg.push_back(adj2);
+	}
+    }
+#ifdef DEBUG_BLEND
+  if (opposite_reg.size() > 0)
+    {
+      std::ofstream of1("opposite_reg.g2");
+      for (size_t ki=0; ki<opposite_reg.size(); ++ki)
+	opposite_reg[ki]->writeRegionPoints(of1);
+    }
+#endif
+  
+  double blendfac = 2.0;
+  for (size_t ki=0; ki<cand_adj.size(); ++ki)
+    {
+      RevEngEdge* edg1 = cand_adj[ki]->getBlendEdge();
+      RevEngRegion *adj1, *adj2;
+      edg1->getAdjacent(adj1, adj2);
+      if ((!adj1) || (!adj2))
+	continue;
+      shared_ptr<ParamSurface> surf1 = cand_adj[ki]->getSurface(0)->surface();
+      if (surf1->instanceType() != Class_Cylinder)
+	continue;
+      if (!surf1->isBounded())
+	{
+	  shared_ptr<Cylinder> cyl =
+	    dynamic_pointer_cast<Cylinder,ParamSurface>(surf1);
+	  cyl->setParamBoundsV(-0.5*diag, 0.5*diag);
+	}
+      shared_ptr<ElementarySurface> elem1 =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+      Point dir1 = elem1->direction();
+      
+      for (size_t kj=0; kj<opposite_reg.size(); ++kj)
+	{
+	  if (!opposite_reg[kj]->hasSurface())
+	    continue;
+	  if (opposite_reg[kj] == adj1 || opposite_reg[kj] == adj2)
+	    continue;
+	  shared_ptr<ParamSurface> surf2 =
+	    opposite_reg[kj]->getSurface(0)->surface();
+	  if (surf2->instanceType() != Class_Plane)
+	    continue;
+	  shared_ptr<ElementarySurface> elem2 =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+	  if (!elem2->isBounded())
+	    elem2->setParameterBounds(-0.5*diag, -0.5*diag, 0.5*diag, 0.5*diag);
+	  Point dir2 = elem2->direction();
+	  double ang = dir1.angle(dir2);
+	  ang = fabs(pihalf - ang);
+	  if (ang > blendfac*angtol)
+	    {
+	      double usz, vsz;
+	      elem1->estimateSfSize(usz, vsz);
+	      double lenlim = 0.9*usz;
+
+	      size_t kr, kh;
+	      for (kr=0; kr<regions_.size(); ++kr)
+		if (regions_[kr].get() == cand_adj[ki])
+		  break;
+	      if (kr == regions_.size())
+		continue;
+	      for (kh=0; kh<regions_.size(); ++kh)
+		if (regions_[kh].get() == opposite_reg[kj])
+		  break;
+	      if (kh == regions_.size())
+		continue;
+#ifdef DEBUG_BLEND
+	      std::ofstream of2("corner_adj.g2");
+	      regions_[kr]->writeRegionPoints(of2);
+	      regions_[kr]->writeSurface(of2);
+	      regions_[kh]->writeRegionPoints(of2);
+	      regions_[kh]->writeSurface(of2);
+#endif
+	      vector<shared_ptr<RevEngEdge> > edges =
+		defineEdgesBetween(kr, elem1, dir1, kh, elem2, dir2, false,
+				   lenlim, false);
+	      if (edges.size() > 0)
+		{
+		  size_t ix2 = edges_.size();
+		  edges_.insert(edges_.end(), edges.begin(), edges.end());
+		  bool found = createTorusBlend(ix2);
+		  if (!found)
+		    edges_.erase(edges_.begin()+ix2, edges_.end());
+		}
+	    }
+	  
+	}
+    }
+   int stop_break = 1;
+}
+
+
+//===========================================================================
+bool getAdjacentToTorus(RevEngEdge* edge, vector<RevEngEdge*>& rev_edgs,
+			double tol, RevEngEdge*& adj_edg1,
+			RevEngEdge*& adj_edg2, double& rad1, double& rad2)
+//===========================================================================
+{
+  adj_edg1 = adj_edg2 = 0;
+  rad1 = rad2 = -1.0;
+  
+  Point pos1, pos2;
+  edge->getCrvEndPoints(pos1, pos2);
+
+  double mindist1 = std::numeric_limits<double>::max();
+  double mindist2 = std::numeric_limits<double>::max();
+  int min_ix1 = -1, min_ix2 = -1;
+  for (size_t ki=0; ki<rev_edgs.size(); ++ki)
+    {
+      RevEngRegion *reg = rev_edgs[ki]->getBlendRegSurf();
+      if (!reg)
+	continue;
+
+      double tpar1, tpar2, dist1, dist2;
+      Point close1, close2;
+      rev_edgs[ki]->closestPoint(pos1, tpar1, close1, dist1);
+      rev_edgs[ki]->closestPoint(pos2, tpar2, close2, dist2);
+      if (dist1 < mindist1)
+	{
+	  mindist1 = dist1;
+	  min_ix1 = (int)ki;
+	}
+       if (dist2 < mindist2)
+	{
+	  mindist2 = dist2;
+	  min_ix2 = (int)ki;
+	}
+      int stop_break = 1;
+    }
+  if (min_ix1 < 0 && min_ix2 < 0)
+    return false;
+  if (mindist1 > tol && mindist2 > tol)
+    return false;   // Might need to tune tolerance
+
+  if (min_ix1 >= 0 && mindist1 <= tol)
+    {
+      adj_edg1 = rev_edgs[min_ix1];
+      RevEngRegion *reg = adj_edg1->getBlendRegSurf();
+      if (reg->hasSurface())
+	{
+	  shared_ptr<ParamSurface> bsurf = reg->getSurface(0)->surface();
+	  shared_ptr<ElementarySurface> belem =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(bsurf);
+	  if (bsurf->instanceType() == Class_Cylinder /*||
+							bsurf->instanceType() == Class_Cone*/)
+	    rad1 = belem->radius(0.0, 0.0);
+	  else if (bsurf->instanceType() == Class_Torus)
+	    rad1 = belem->radius2(0.0, 0.0);
+	}
+    }
+  if (min_ix2 >= 0 && mindist2 <= tol)
+    {
+      adj_edg2 = rev_edgs[min_ix2];
+      RevEngRegion *reg = adj_edg2->getBlendRegSurf();
+      if (reg->hasSurface())
+	{
+	  shared_ptr<ParamSurface> bsurf = reg->getSurface(0)->surface();
+	  shared_ptr<ElementarySurface> belem =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(bsurf);
+	  if (bsurf->instanceType() == Class_Cylinder /*||
+							bsurf->instanceType() == Class_Cone*/)
+	    rad2 = belem->radius(0.0, 0.0);
+	  else if (bsurf->instanceType() == Class_Torus)
+	    rad2 = belem->radius2(0.0, 0.0);
+	}
+    }
+
+  if (rad1 < 0 && rad2 < 0)
+    return false;
+
+  return true;
+}
+
+//===========================================================================
+bool RevEng::createTorusBlend(size_t ix)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  double angtol = 5.0*anglim_;
+  double pihalf = 0.5*M_PI;
+  double tol2 = 2*approx_tol_;  // Due to possible inaccuracies
+  double tol5 = 5*approx_tol_;  // Due to possible inaccuracies
+
+  // Adjacent regions
+  RevEngRegion* adj[2];
+  edges_[ix]->getAdjacent(adj[0], adj[1]);
+
+  if ((!adj[0]->hasSurface()) || (!adj[1]->hasSurface()))
+    return false;  // Something wrong
+  
+  vector<shared_ptr<CurveOnSurface> > intcv1, intcv2;
+  edges_[ix]->getCurve(intcv1, true);
+  edges_[ix]->getCurve(intcv2, false);
+  
+  bool out1 = false, out2 = false;
+  edges_[ix]->getOuterInfo(out1, out2);
+  
+  shared_ptr<ParamSurface> surf[2];
+  surf[0] = adj[0]->getSurface(0)->surface();
+  surf[1] = adj[1]->getSurface(0)->surface();
+  ClassType type[2];
+  type[0] = surf[0]->instanceType();
+  type[1] = surf[1]->instanceType();
+  int jx1 = (type[0] == Class_Plane) ? 0 :
+    ((type[1] == Class_Plane) ? 1 : -1);
+  if (jx1 == -1)
+    return false;
+  int jx2 = 1 - jx1;
+  if (type[jx2] != Class_Cylinder)
+    return false;
+  bool outp = (jx1 == 0) ? out1 : out2;
+  bool outr = (jx1 == 0) ? out2 : out1;
+  shared_ptr<Cylinder> cyl = dynamic_pointer_cast<Cylinder,ParamSurface>(surf[jx2]);
+  if (!cyl.get())
+    return false;  // Should not happen
+  double radius1 = cyl->getRadius();
+
+  // Get adjacent blend edges and corresponding radii
+  vector<RevEngEdge*> rev_edgs = adj[jx1]->getAllRevEdges();
+  if (rev_edgs.size() == 0)
+    return false;
+
+  double rad1 = -1.0, rad2 = -1.0;
+  RevEngEdge *revedg1, *revedg2;
+  bool OK = getAdjacentToTorus(edges_[ix].get(), rev_edgs, tol5, 
+			       revedg1, revedg2, rad1, rad2);
+// #ifdef DEBUG_BLEND
+//   if (!OK)
+//     std::cout << "getAdjacentToTorus not OK" << std::endl;
+//   if (!revedg1)
+//     std::cout << "getAdjacentToTorus, revedg1 missing" << std::endl;
+//   if (!revedg2)
+//     std::cout << "getAdjacentToTorus, revedg2 missing" << std::endl;
+// #endif
+  if (!OK)
+    return false;
+  if ((!revedg1) || (!revedg2))
+    return false;
+  bool possible_suitcase =
+    (fabs(rad1-rad2) > std::min(fabs(radius1-rad1), fabs(radius1-rad2)));
+
+  double radius2 = (rad1 > 0.0 && rad2 > 0.0) ? 0.5*(rad1 + rad2) :
+    ((rad1 > 0.0) ? rad1 : rad2);  // Should do extra checking if rad1 very
+  // different from rad2 and both are larger than zero
+
+  Point loc1 = cyl->getLocation();
+  Point axis1 = cyl->getAxis();
+  Point Cx = cyl->direction2();
+
+  // The torus location coincides with the intersection point between the
+  // cylinder axis and the plane
+  shared_ptr<Plane> plane = dynamic_pointer_cast<Plane,ParamSurface>(surf[jx1]);
+  Point dirp = plane->direction();
+  Point avnorm = adj[jx1]->getMeanNormal();
+  if (dirp*avnorm < 0.0)
+    dirp *= -1;
+  if (axis1*dirp < 0.0)
+    axis1 *= -1;
+  double alpha = axis1.angle(dirp);
+  Point locp = plane->location();
+  Point loct0 = loc1 + ((locp - loc1)*axis1)*axis1;
+  double d2 = locp.dist(loct0);
+  double xlen = d2*atan(alpha);
+  int sgn1 = ((locp - loct0)*dirp < 0.0) ? -1 : 1;
+  loct0 += sgn1*xlen*dirp;
+  int sgn2 = outp ? 1 : -1;
+  Point loct = loct0 + sgn2*radius2*dirp;
+  int sgn3 = outr ? -1 : 1;
+  double radiust = radius1 + sgn3*radius2;
+  if (radius2 >= radiust)
+    return false;  // Not expecting degenerate torus
+
+  shared_ptr<Torus> torus(new Torus(radiust, radius2, loct, dirp, Cx));
+
+  // Regions in blend area
+  vector<RevEngRegion*> blend_regs;
+  edges_[ix]->getAllBlendRegs(blend_regs);
+#ifdef DEBUG_BLEND
+  std::ofstream of("torus_blend2.g2");
+  for (size_t ki=0; ki<blend_regs.size(); ++ki)
+    blend_regs[ki]->writeRegionPoints(of);
+#endif
+  
+  // Parameterize on torus
+  vector<RevEngPoint*> blend_pts;
+  for (size_t ki=0; ki<blend_regs.size(); ++ki)
+    blend_pts.insert(blend_pts.end(), blend_regs[ki]->pointsBegin(),
+		     blend_regs[ki]->pointsEnd());
+  double maxd, avd;
+  int num_in, num2_in;
+  vector<double> parvals;
+  vector<pair<double,double> > dist_ang;
+  vector<RevEngPoint*> inpt, outpt;
+  RevEngUtils::distToSurf(blend_pts.begin(), blend_pts.end(), torus,
+			  approx_tol_, maxd, avd, num_in, num2_in, inpt, outpt,
+			  parvals, dist_ang, angtol);
+  
+  RectDomain dom = cyl->getParameterBounds();
+  if (sgn3 < 0)
+    torus->setParameterBounds(dom.umin(), 0.0, dom.umax(), pihalf);
+  else
+    torus->setParameterBounds(dom.umin(), M_PI, dom.umax(), 1.5*M_PI);
+  // What if the cylinder and the plane are not perpendicular? It is not a
+  // torus, but it is anyway not handled
+  
+#ifdef DEBUG_BLEND
+  torus->writeStandardHeader(of);
+  torus->write(of);
+  vector<shared_ptr<ParamCurve> > c_cvs = torus->constParamCurves(0.0, true);
+  c_cvs[0]->writeStandardHeader(of);
+  c_cvs[0]->write(of);
+#endif
+
+  // Define longitudinal boundary edges of torus
+  RectDomain tordom = torus->getParameterBounds();
+  double torlim[4];
+  torlim[0] = tordom.umin();
+  torlim[1] = tordom.umax();
+  torlim[2] = tordom.vmin();
+  torlim[3] = tordom.vmax();
+  vector<shared_ptr<CurveOnSurface> > torbound(4);
+  for (int ka=0; ka<2; ++ka)
+    {
+      shared_ptr<Circle> circle = torus->getMajorCircle(torlim[2+ka]);
+      Point mid(torlim[0], torlim[2+ka]);
+      Point vec = Point(torlim[1],torlim[2+ka]) - Point(torlim[0],torlim[2+ka]);
+      vec.normalize();
+      shared_ptr<Line> line(new Line(mid, vec));
+      line->setParamBounds(0.0, torlim[1]-torlim[0]);
+      torbound[2+ka] =
+	shared_ptr<CurveOnSurface>(new CurveOnSurface(torus, line, circle, false,
+						      3, 2, torlim[2+ka], 2+ka, true));
+    }
+
+  // Restrict adjacent cylinders
+  // The cylinder and the edges does not necessarily have the same
+  // parameterization
+  Point pt1, pt2;
+  double midpar = 0.5*(tordom.vmin() + tordom.vmax());
+  torus->point(pt1, tordom.umin(), midpar);
+  torus->point(pt2, tordom.umax(), midpar);
+  vector<RevEngRegion*> adj_reg(2, 0);
+  bool upper2[2];
+  double lim[2];
+  vector<shared_ptr<ElementarySurface> > adj_surf(2);
+  vector<double> parbound(8);
+  int kx[2];
+  for (int ka=0; ka<2; ++ka)
+    {
+      if ((ka == 0 && rad1 <= 0.0) || (ka == 1 && rad2 <= 0.0))
+	{
+	  kx[ka] = -1;
+	  continue;
+	}
+
+      RevEngRegion *reg = (ka == 0) ? revedg1->getBlendRegSurf() :
+	revedg2->getBlendRegSurf();
+      shared_ptr<ParamSurface> bsurf = reg->getSurface(0)->surface();
+      shared_ptr<ElementarySurface> belem =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(bsurf);
+      adj_reg[ka] = reg;
+      adj_surf[ka] = belem;
+      
+     double reg_dom[4];
+      reg->getDomain(reg_dom);
+      Point pt3[2];
+      int kx1 = (bsurf->instanceType() == Class_Cylinder) ? 0 : 1;
+      for (int kb=0; kb<2; ++kb)
+	{
+	  double par[2];
+	  par[kx1] = 0.5*(reg_dom[2*kx1] + reg_dom[2*kx1+1]);
+	  par[1-kx1] = reg_dom[2*(1-kx1)+kb];
+	  pt3[kb] = bsurf->point(par[0], par[1]);
+	}
+      
+      double upar1, vpar1, bdist1, upar2, vpar2, bdist2;
+      Point bclose1, bclose2;
+      bsurf->closestPoint(pt1, upar1, vpar1, bclose1, bdist1, eps);
+      bsurf->closestPoint(pt2, upar2, vpar2, bclose2, bdist2, eps);
+      double uvpar;
+      if (belem->instanceType() == Class_Cylinder)
+	uvpar = (bdist1 <= bdist2) ? vpar1 : vpar2;
+      else
+	uvpar = (bdist1 <= bdist2) ? upar1 : upar2;
+      Point tor_pt = (bdist1 <= bdist2) ? pt1 : pt2;
+      lim[ka] = uvpar;
+      kx[ka] = (bdist1 <= bdist2) ? 0 : 1;
+
+      // Assume the part of the cylinder to remove is the smaller one
+      RectDomain bdom = bsurf->containingDomain();
+      parbound[4*ka] = bdom.umin();
+      parbound[4*ka+1] = bdom.vmin();
+      parbound[4*ka+2] = bdom.umax();
+      parbound[4*ka+3] = bdom.vmax();
+      if (tor_pt.dist(pt3[1]) < tor_pt.dist(pt3[0]))
+	{
+	  if (belem->instanceType() == Class_Cylinder)
+	    parbound[4*ka+3] = uvpar;
+	  else
+	    parbound[4*ka+2] = uvpar;
+	  upper2[ka] = true;
+	}
+      else 
+	{
+	  if (belem->instanceType() == Class_Cylinder)
+	    parbound[4*ka+1] = uvpar;
+	  else
+	    parbound[4*ka] = uvpar;
+	  upper2[ka] = false;
+	}
+#ifdef DEBUG_BLEND
+      bsurf->writeStandardHeader(of);
+      bsurf->write(of);
+#endif
+    }
+
+  Point vec1 = (adj_surf[0].get()) ? adj_surf[0]->location() - plane->location() : Point(0.0, 0.0, 0.0);
+  Point vec2 = (adj_surf[1].get()) ? adj_surf[1]->location() - plane->location() : Point(0.0, 0.0, 0.0);
+  double sc1 = vec1*dirp;
+  double sc2 = vec2*dirp;
+  if (sc1*sc2 < 0.0) //kx[0] == kx[1] && kx[0]>=0)
+    {
+      // Need to wait with "setParameterBounds"
+      vector<RevEngRegion*> blends(3);
+      blends[0] = adj[jx2];
+      blends[1] = revedg1->getBlendRegSurf();
+      blends[2] = revedg2->getBlendRegSurf();
+      return suitcaseCorner(blends, edges_[ix].get());
+    }
+  else if (possible_suitcase)
+    return false;  // Not an expected configuration
+
+  if (kx[0] == kx[1])
+    return false;   // Not an expected configuration
+
+  for (int ka=0; ka<2; ++ka)
+    {
+      if (adj_surf[ka].get())
+	{
+	  adj_surf[ka]->setParameterBounds(parbound[4*ka], parbound[4*ka+1],
+					   parbound[4*ka+2], parbound[4*ka+3]);
+	  adj_reg[ka]->adaptEdges();
+	}
+   }
+
+  // Bound blend cylinder
+  Point pt3;
+  torus->point(pt3, 0.5*(dom.umin()+dom.vmin()), 0);
+  double upar3, vpar3, dist3;
+  Point close3;
+  bool upper = false;
+  cyl->closestPoint(pt3, upar3, vpar3, close3, dist3, eps);
+  if (fabs(vpar3-dom.vmin()) < fabs(dom.vmax()-vpar3))
+    cyl->setParamBoundsV(vpar3, dom.vmax());
+  else
+    {
+      cyl->setParamBoundsV(dom.vmin(), vpar3);
+      upper = true;
+    }
+  adj[jx2]->adaptEdges();
+  
+ #ifdef DEBUG_BLEND
+  cyl->writeStandardHeader(of);
+  cyl->write(of);
+#endif
+
+  shared_ptr<Circle> bcircle = cyl->getCircle(vpar3);
+  shared_ptr<ElementaryCurve> bpar =
+    cyl->getElementaryParamCurve(bcircle.get(), approx_tol_);
+  shared_ptr<CurveOnSurface> blendbound(new CurveOnSurface(cyl, bpar, bcircle,
+							   false, 3, 2, vpar3,
+							   (upper) ? 3 : 2, true));
+ 
+  // Define traverse boundary edges of torus and adjacent cylinders
+  vector<shared_ptr<CurveOnSurface> > cylbound(2);
+  for (int ka=0; ka<2; ++ka)
+    {
+      if (!adj_reg[ka])
+	continue;
+
+      bool udir = (adj_surf[ka]->instanceType() == Class_Cylinder) ? true : false;
+      shared_ptr<Circle> circle = torus->getMinorCircle(torlim[ka]);
+      Point mid(torlim[ka], torlim[2]);
+      Point vec = Point(torlim[ka],torlim[3]) - Point(torlim[ka],torlim[2]);
+      vec.normalize();
+      shared_ptr<Line> line(new Line(mid, vec));
+      line->setParamBounds(0.0, torlim[3]-torlim[2]);
+      torbound[ka] =
+	shared_ptr<CurveOnSurface>(new CurveOnSurface(torus, line, circle,
+						      false, 3, 1, torlim[ka],
+						      ka, true));
+      vector<shared_ptr<ParamCurve> > tmpcv =
+	adj_surf[ka]->constParamCurves(lim[ka], udir);
+      if (tmpcv.size() != 1)
+	continue;  // Should not happen
+      shared_ptr<ElementaryCurve> space =
+	dynamic_pointer_cast<ElementaryCurve,ParamCurve>(tmpcv[0]);
+      shared_ptr<ElementaryCurve> par =
+	adj_surf[ka]->getElementaryParamCurve(space.get(), approx_tol_);
+      int bd;
+      if (udir)
+	bd = (upper2[ka]) ? 3 : 2;
+      else
+	bd = (upper2[ka]) ? 1 : 0;
+      shared_ptr<ParamCurve> par2;
+      if (!par.get())
+	{
+	  Point pt1(2), pt2(2);
+	  if (udir)
+	    {
+	      pt1[1] = pt2[1] = lim[ka];
+	      pt1[0] = parbound[4*ka];
+	      pt2[0] = parbound[4*ka+2];
+	    }
+	  else
+	    {
+	      pt1[0] = pt2[0] = lim[ka];
+	      pt1[1] = parbound[4*ka+1];
+	      pt2[1] = parbound[4*ka+3];
+	    }
+	  par2 = shared_ptr<ParamCurve>(new SplineCurve(pt1, space->startparam(),
+							pt2, space->endparam()));
+	}
+      cylbound[ka] =
+	shared_ptr<CurveOnSurface>(new CurveOnSurface(adj_surf[ka],
+						      par.get() ? par : par2,
+						      space,
+						      false, 3, udir ? 1 : 2,
+						      lim[ka],
+						      bd, true));
+    }
+
+  // Define planar boundary curve
+  shared_ptr<CurveOnSurface> planebound;
+  shared_ptr<ParamCurve> pspace1 = torbound[3]->spaceCurve();
+  shared_ptr<Circle> pspace = dynamic_pointer_cast<Circle,ParamCurve>(pspace1);
+  shared_ptr<ElementaryCurve> ppar =
+    plane->getElementaryParamCurve(pspace.get(), 10.0*tol2);  // Just to test
+  planebound = shared_ptr<CurveOnSurface>(new CurveOnSurface(plane, ppar,
+							     pspace, false, 1));
+
+#ifdef DEBUG_BLEND
+  std::ofstream ofc("torus_trim.g2");
+  for (int ka=0; ka<4; ++ka)
+    {
+      if (!torbound[ka].get())
+	continue;
+      shared_ptr<ParamCurve> tmp = torbound[ka]->spaceCurve();
+      tmp->writeStandardHeader(ofc);
+      tmp->write(ofc);
+    }
+  for (int ka=0; ka<2; ++ka)
+    {
+      if (!cylbound[ka].get())
+	continue;
+      shared_ptr<ParamCurve> tmp = cylbound[ka]->spaceCurve();
+      tmp->writeStandardHeader(ofc);
+      tmp->write(ofc);
+    }
+  if (planebound.get())
+    {
+      shared_ptr<ParamCurve> tmp = planebound->spaceCurve();
+      tmp->writeStandardHeader(ofc);
+      tmp->write(ofc);
+    }
+
+  std::ofstream ofp("torus_par.g2");
+  for (int ka=0; ka<4; ++ka)
+    {
+      if (!torbound[ka].get())
+	continue;
+      shared_ptr<ParamCurve> tmp = torbound[ka]->parameterCurve();
+      if (!tmp.get())
+	continue;
+      SplineDebugUtils::writeSpaceParamCurve(tmp, ofp, 0.0);
+    }
+#endif
+  
+  // Move points as appropriate
+  // From torus
+  vector<vector<RevEngPoint*> > move1(4);
+  vector<RevEngPoint*> torus_pts;
+  for (size_t ki=0; ki<blend_pts.size(); ++ki)
+    {
+      if (parvals[2*ki+1] > M_PI)
+	move1[0].push_back(blend_pts[ki]);  // To blend cylinder
+      else if (parvals[2*ki+1] > pihalf)
+	move1[1].push_back(blend_pts[ki]);  // To plane
+      else if (parvals[2*ki] < dom.umin())
+	move1[2].push_back(blend_pts[ki]);  // First adjacent cylinder
+      else if (parvals[2*ki] > dom.umax())
+	move1[3].push_back(blend_pts[ki]);  // Second adjacent cylinder
+      else
+	{
+	  blend_pts[ki]->setPar(Vector2D(parvals[2*ki],parvals[2*ki+1]));
+	  blend_pts[ki]->setSurfaceDist(dist_ang[ki].first, dist_ang[ki].second);
+	  torus_pts.push_back(blend_pts[ki]);
+	}
+    }
+
+  // From blend cylinder
+  vector<RevEngPoint*> move_cyl;
+  int num_pts_cyl = adj[jx2]->numPoints();
+  for (int ka=0; ka<num_pts_cyl; ++ka)
+    {
+      RevEngPoint* curr = adj[jx2]->getPoint(ka);
+      Vector2D par = curr->getPar();
+      if ((upper && par[1] > vpar3) || (upper == false && par[1] < vpar3))
+	move_cyl.push_back(curr);
+    }
+
+  // Remove identified points from cylinder
+  if (move_cyl.size() > 0)
+    adj[jx2]->removeAndUpdatePoints(move_cyl);
+
+  // From adjacent cylinders
+  vector<vector<RevEngPoint*> > move_adj(2);
+  for (int kb=0; kb<2; ++kb)
+    {
+      if (!adj_reg[kb])
+	continue;
+      int num_pts_cyl = adj_reg[kb]->numPoints();
+      for (int ka=0; ka<num_pts_cyl; ++ka)
+	{
+	  RevEngPoint* curr = adj_reg[kb]->getPoint(ka);
+	  Vector2D par = curr->getPar();
+	  if ((upper2[kb] && par[1] > lim[kb]) ||
+	      (upper2[kb] == false && par[1] < lim[kb]))
+	    move_adj[kb].push_back(curr);
+	}
+      if (move_adj[kb].size() > 0)
+	adj_reg[kb]->removeAndUpdatePoints(move_adj[kb]);
+    }
+
+  // From adjacent plane
+  vector<RevEngPoint*> move_plane;
+  adj[jx1]->extractOutOfEdge(planebound, (jx1 == 0) ? intcv1 : intcv2,
+			     radius2, approx_tol_, angtol, move_plane);
+
+  // To plane
+  bool OK1, OK2, OK3;
+  if (move1[1].size() > 0)
+    OK1 = adj[jx1]->addPointsToGroup(move1[1], approx_tol_, angtol);
+
+  // To blend cylinder
+  if (move1[0].size() > 0)
+    OK2 = adj[jx2]->addPointsToGroup(move1[0], approx_tol_, angtol);
+
+  // To adjacent cylinders
+  vector<vector<RevEngPoint*> > added_groups;
+  for (int ka=0; ka<2; ++ka)
+    if (move1[2+ka].size() > 0)
+      {
+	if (adj_reg[ka])
+	  OK3 = adj_reg[ka]->addPointsToGroup(move1[2+ka], approx_tol_, angtol);
+	else
+	  added_groups.push_back(move1[2+ka]);
+      }
+
+
+  // To torus
+  if (move_plane.size() > 0)
+    torus_pts.insert(torus_pts.end(), move_plane.begin(), move_plane.end());
+  if (move_adj[0].size() > 0)
+    torus_pts.insert(torus_pts.end(), move_adj[0].begin(), move_adj[0].end());
+  if (move_adj[1].size() > 0)
+    torus_pts.insert(torus_pts.end(), move_adj[1].begin(), move_adj[1].end());
+  if (move_cyl.size() > 0)
+    torus_pts.insert(torus_pts.end(), move_cyl.begin(), move_cyl.end());
+  if (torus_pts.size() == 0)
+    return false;
+
+  // Define torus blend
+  shared_ptr<RevEngRegion> blendreg(new RevEngRegion(classification_type_,
+						     edge_class_type_,
+						     torus_pts));
+  blendreg->setRegionAdjacency();
+  regions_.push_back(blendreg);
+  shared_ptr<HedgeSurface> hedge;
+  shared_ptr<ParamSurface> torus_tmp = torus;
+  blendreg->setAssociatedSurface(torus_tmp, approx_tol_, angtol,
+				 min_point_region_, hedge);
+  if (hedge.get())
+    surfaces_.push_back(hedge);
+  
+  if (added_groups.size() > 0)
+    {
+      vector<HedgeSurface*> dummy_hedge;
+      surfaceExtractOutput((int)regions_.size()-1, added_groups, dummy_hedge);
+    }
+
+#ifdef DEBUG_BLEND
+  std::ofstream oft("torus_corner.g2");
+  blendreg->writeRegionPoints(oft);
+  adj[jx1]->writeRegionPoints(oft);
+  adj[jx2]->writeRegionPoints(oft);
+  if (adj_reg[0])
+    adj_reg[0]->writeRegionPoints(oft);
+  if (adj_reg[1])
+    adj_reg[1]->writeRegionPoints(oft);
+#endif
+  
+  // Add edges to regions
+  int status = 0;
+  vector<shared_ptr<ftEdge> > tor_edg(4);
+  for (int ka=0; ka<4; ++ka)
+    {
+      if (torbound[ka].get())
+	{
+	  tor_edg[ka] = shared_ptr<ftEdge>(new ftEdge(hedge.get(), torbound[ka],
+						      torbound[ka]->startparam(),
+						      torbound[ka]->endparam()));
+	  blendreg->addTrimEdge(tor_edg[ka]);
+	}
+    }
+
+  shared_ptr<ftEdge> plane_edg(new ftEdge(adj[jx1]->getSurface(0), planebound,
+					  planebound->startparam(),
+					  planebound->endparam()));
+  adj[jx1]->addTrimEdge(plane_edg);
+  plane_edg->setReversed(true);
+  tor_edg[3]->connectTwin(plane_edg.get(), status);
+
+
+   shared_ptr<ftEdge> cyl_edg(new ftEdge(adj[jx2]->getSurface(0), blendbound,
+					 blendbound->startparam(),
+					 blendbound->endparam()));
+  adj[jx2]->addTrimEdge(cyl_edg);
+  cyl_edg->setReversed(true);
+  tor_edg[2]->connectTwin(cyl_edg.get(), status);
+
+  for (int ka=0; ka<2; ++ka)
+    {
+      if (cylbound[ka].get())
+	{
+	  shared_ptr<ftEdge> adj_edg(new ftEdge(adj_reg[ka]->getSurface(0),
+						cylbound[ka],
+						cylbound[ka]->startparam(),
+						cylbound[ka]->endparam()));
+	  adj_reg[ka]->addTrimEdge(adj_edg);
+	  adj_edg->setReversed(true);
+	  if (kx[ka] >= 0 && tor_edg[kx[ka]].get())
+	    tor_edg[kx[ka]]->connectTwin(adj_edg.get(), status);
+	}
+    }
+
+  for (size_t ki=0; ki<blend_regs.size(); ++ki)
+    {
+      //blend_regs[ki]->removeFromAdjacent();
+      blend_regs[ki]->setRemove();
+    }
+   edges_[ix]->clearBlendRegions();
+ 
+  return true;
+}
+
+//===========================================================================
+void RevEng::setBlendBoundaries(RevEngRegion *reg)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  if (!reg->hasSurface())
+    return;
+
+  shared_ptr<ParamSurface> surf = reg->getSurface(0)->surface();
+  if (surf->instanceType() != Class_Cylinder &&
+      surf->instanceType() != Class_Torus)
+    return;   
+
+  double angtol = 5.0*anglim_;
+  double diag0 = bbox_.low().dist(bbox_.high());
+  
+  // Adjacent regions
+  RevEngEdge *edge = reg->getBlendEdge();
+  RevEngRegion* adj[2];
+  edge->getAdjacent(adj[0], adj[1]);
+
+  if ((!adj[0]->hasSurface()) || (!adj[1]->hasSurface()))
+    return;  // Something wrong
+
+  bool out1 = false, out2 = false;
+  edge->getOuterInfo(out1, out2);
+
+  vector<shared_ptr<CurveOnSurface> > intcv1, intcv2;
+  edge->getCurve(intcv1, true);
+  edge->getCurve(intcv2, false);
+  //double eps1 = approx_tol_; //std::max(1.0e-4, 0.1*approx_tol_);
+  // for (size_t ki=0; ki<intcv1.size(); ++ki)
+  //   intcv1[ki]->fixMismatchCurves(eps1);
+  // for (size_t ki=0; ki<intcv2.size(); ++ki)
+  //   intcv2[ki]->fixMismatchCurves(eps1);
+  
+#ifdef DEBUG_BLEND
+  std::ofstream of("blend.g2");
+  reg->writeRegionPoints(of);
+  surf->writeStandardHeader(of);
+  surf->write(of);
+
+  adj[0]->writeRegionPoints(of);
+  adj[1]->writeRegionPoints(of);
+#endif
+
+  // Extract longitudial boundary curves
+  shared_ptr<ElementarySurface> elem =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+  //double radius = elem->radius(0.0, 0.0);
+
+  vector<shared_ptr<ElementarySurface> > adj_elem(2);
+  for (int ka=0; ka<2; ++ka)
+    {
+      shared_ptr<ParamSurface> tmp = adj[ka]->getSurface(0)->surface();
+      adj_elem[ka] = dynamic_pointer_cast<ElementarySurface,ParamSurface>(tmp);
+    }
+
+#ifdef DEBUG_BLEND
+  std::ofstream ofsf("adj_sfs.g2");
+  adj_elem[0]->writeStandardHeader(ofsf);
+  adj_elem[0]->write(ofsf);
+  shared_ptr<ParamCurve> tmp_cv1 = intcv1[0]->spaceCurve();
+  tmp_cv1->writeStandardHeader(ofsf);
+  tmp_cv1->write(ofsf);
+  adj_elem[1]->writeStandardHeader(ofsf);
+  adj_elem[1]->write(ofsf);
+  shared_ptr<ParamCurve> tmp_cv2 = intcv2[0]->spaceCurve();
+  tmp_cv2->writeStandardHeader(ofsf);
+  tmp_cv2->write(ofsf);
+#endif
+
+  Point posi1, posi2, pos;
+  intcv1[0]->point(posi1, intcv1[0]->startparam());
+  double pari = (edge->isClosed(approx_tol_)) ?
+    0.5*(intcv1[0]->startparam() + intcv1[intcv1.size()-1]->endparam()) :
+    intcv1[intcv1.size()-1]->endparam();
+  intcv1[intcv1.size()-1]->point(posi2, pari);
+  pos = 0.5*(posi1 + posi2);
+  RectDomain surfdom = surf->containingDomain();
+  double regdom[4];
+  reg->getDomain(regdom);
+  double regfac = 2.0;
+  double rad;
+  double tpar1, tpar2;
+  bool udir;
+  int constdir;
+  double delfac = 0.6;
+  double seamfac = 0.1;
+  bool plane1 =  (adj_elem[0]->instanceType() == Class_Plane);
+  bool plane2 =  (adj_elem[1]->instanceType() == Class_Plane);
+  if (surf->instanceType() == Class_Cylinder)
+    {
+      Point norm1 = adj_elem[0]->direction();
+      Point norm2 = adj_elem[1]->direction();
+      double ang = norm1.angle(norm2);
+      ang = std::min(ang, M_PI-ang);
+      
+      tpar1 = M_PI - 0.5*ang; 
+      tpar2 = tpar1 + ang;
+      shared_ptr<Cylinder> cyl = dynamic_pointer_cast<Cylinder,ParamSurface>(surf);
+      cyl->setParamBoundsU(tpar1, tpar2);
+      double tdelreg = regdom[3] - regdom[2];
+      if (tdelreg < regfac*approx_tol_)
+	tdelreg = 0.5*diag0;
+      if (surfdom.vmax() - surfdom.vmin() > regfac*tdelreg)
+	cyl->setParamBoundsV(std::max(surfdom.vmin(), regdom[2]-delfac*tdelreg),
+			     std::min(surfdom.vmax(), regdom[3]+delfac*tdelreg));
+      rad = cyl->getRadius();
+      udir = false;
+      constdir = 0;
+    }
+  else
+    {
+      shared_ptr<Torus> tor = dynamic_pointer_cast<Torus,ParamSurface>(surf);
+      RectDomain tor_dom = tor->getParameterBounds();
+      if (plane1 || plane2)
+	{
+	  int plane_ix = (plane1) ? 0 : 1;
+	  if (adj_elem[plane_ix]->instanceType() != Class_Plane)
+	    return;  // Not as expected
+	  rad = tor->getMinorRadius();
+	  bool plane_out = (plane_ix == 0) ? out1 : out2;
+	  bool rot_out = (plane_ix == 0) ? out2 : out1;
+	  double phi = 0.5*M_PI;
+	  Point norm = adj_elem[plane_ix]->direction();
+	  Point norm2 = adj[plane_ix]->getMeanNormalTriang();
+	  double beta = 0.5; //(norm*norm2 > 0.0) ? 0.5 : 0.0;
+	  if (adj_elem[1-plane_ix]->instanceType() == Class_Cone)
+	    {
+	      shared_ptr<Cone> cone =
+		dynamic_pointer_cast<Cone,ElementarySurface>(adj_elem[1-plane_ix]);
+	      double alpha = cone->getConeAngle();
+	      Point axis = adj_elem[1-plane_ix]->direction();
+	      int sgn = (norm*axis < 0.0) ? -1 : 1;
+	      phi += sgn*alpha;
+	    }
+	  if (rot_out)
+	    {
+	      tpar2 = tor_dom.vmin()+(1.0+beta)*M_PI;
+	      tpar1 = tpar2 - phi;
+	    }
+	  else
+	    {
+	      tpar1 = tor_dom.vmin()+(1.0-beta)*M_PI;
+	      tpar2 = tpar1 + phi;
+	    }
+	  if (plane_out)
+	    {
+	      int sgn = 1; //(norm*norm2 < 0.0 && rot_out == false) ? -1 : 1;
+	      tpar1 += 0.5*sgn*M_PI;
+	      tpar2 += 0.5*sgn*M_PI;
+	    }
+	}
+      else
+	{
+	  int cyl_ix = (adj_elem[0]->instanceType() == Class_Cylinder) ? 0 : 1;
+	  int cone_ix = 1 - cyl_ix;
+	  if (adj_elem[cyl_ix]->instanceType() != Class_Cylinder ||
+	      adj_elem[cone_ix]->instanceType() != Class_Cone)
+	    return;
+
+	  shared_ptr<Cone> cone =
+	    dynamic_pointer_cast<Cone,ElementarySurface>(adj_elem[cone_ix]);
+	  double alpha = cone->getConeAngle();
+	  double beta = M_PI - fabs(alpha);
+	  double phi = 0.5*(M_PI - beta);
+	  rad = tor->getMinorRadius();
+	  Point axis = adj_elem[cyl_ix]->direction();
+	  Point loc = adj_elem[cyl_ix]->location();
+	  double rd = (pos-loc)*axis;
+	  Point centr = loc + rd*axis;
+	  Point loc2 = tor->location();
+	  Point loc2_2 = centr + ((loc2 - centr)*axis)*axis;
+	  double hh = loc2_2.dist(centr);
+	  double d2 = hh/sin(phi) - rad;
+	  double xlen = sqrt((rad+d2)*(rad+d2) - rad*rad);
+	  tpar1 = tor_dom.vmin() + M_PI - 2.0*xlen;
+	  tpar2 = tpar1 + 2*xlen;
+	}
+      
+      double tdelreg = regdom[1] - regdom[0];
+      double upar1 = tor_dom.umin();
+      double upar2 = tor_dom.umax();
+      double umid = 0.5*(regdom[0]+regdom[1]);
+      double eps2 = std::max(eps, 0.001*(upar2-upar1));
+
+      double cp1, cp2;
+      Point cpos1, cpos2;
+      double seed[2];
+      seed[0] = 0.5*(regdom[0]+regdom[1]);
+      seed[1] = 0.5*(regdom[2]+regdom[3]);
+      int seam1 = edge->closedSfAtEnd(approx_tol_, cp1, cpos1, true);
+      int seam2 = edge->closedSfAtEnd(approx_tol_, cp2, cpos2, false);
+      double dd = cpos1.dist(cpos2);
+      double u1, v1, u2, v2, d1, d2;
+      Point cl1, cl2;
+      elem->closestPoint(cpos1, u1, v1, cl1, d1, eps, 0, seed);
+      elem->closestPoint(cpos2, u2, v2, cl2, d2, eps, 0, seed);
+      if (u2 < u1)
+	std::swap(u1, u2);
+      if ((u2-u1 < tdelreg-eps || umid > u2 || umid < u1) && (seam1 || seam2))
+	{
+	  // Check parameter at seam
+	  if (seam1 && u1-upar1 < eps2)
+	    u1 = upar2;
+	  else if (seam2 && upar2-u2 < eps2)
+	    u2 = upar1;
+	  if (u2 < u1)
+	    std::swap(u1, u2);
+	}
+	  
+      if (seam1 != seam2 && dd > approx_tol_)
+	{
+	  bool close1, close2;
+	  tor->isClosed(close1, close2);  // Expects close1 = true
+	  if (close1)
+	    {
+	      if (tdelreg > u2-u1)
+		{
+		  int stop_seam = 1;
+		}
+	      
+	      // Check for seam. Solution could be improved
+	      if (fabs(u1-upar1) < eps2 && umid > u2) // && upar2 < u2)
+		u1 = upar2;
+	      else if (fabs(upar2-u1) < eps2 && umid < u2)
+		u1 = upar1;
+	      if (fabs(u2-upar1) < eps2 && umid > u1)
+		u2 = upar2;
+	      else if (fabs(upar2-u2) < eps2 && umid < u1) // && upar1 > u1)
+		u2 = upar1;
+
+	      if (u2 < u1)
+		std::swap(u1, u2);
+	    }
+	      
+	  upar1 = std::max(upar1, u1);
+	  upar2 = std::min(upar2, u2);
+	}
+      
+      if (upar2 - upar1 > regfac*tdelreg)
+	{
+	  upar1 = std::max(upar1, std::min(regdom[0]-delfac*tdelreg, u1));
+	  upar2 = std::min(upar2, std::max(regdom[1]+delfac*tdelreg, u2));
+	  if (upar1 < seamfac)
+	    upar1 = 0.0;
+	  if (2*M_PI-upar2 < seamfac)
+	    upar2 = 2*M_PI;
+	}
+      tor->setParameterBounds(upar1, tpar1, upar2, tpar2);
+      udir = true;
+      constdir = 1;
+    }
+
+
+  vector<shared_ptr<ParamCurve> > bdcv1 = elem->constParamCurves(tpar1, udir); 
+  vector<shared_ptr<ParamCurve> > bdcv2 = elem->constParamCurves(tpar2, udir);
+  if (bdcv1.size() != 1 || bdcv2.size() != 1)
+    return;  // Something strange
+
+  shared_ptr<ElementaryCurve> space[2];
+  space[0] = dynamic_pointer_cast<ElementaryCurve,ParamCurve>(bdcv1[0]);
+  space[1] = dynamic_pointer_cast<ElementaryCurve,ParamCurve>(bdcv2[0]);
+  if ((!space[0].get()) || (!space[1].get()))
+    return;
+  BoundingBox bb1 = adj[0]->getBbox();
+  BoundingBox bb2 = adj[1]->getBbox();
+  double diag = std::min(bb1.low().dist(bb1.high()), bb2.low().dist(bb2.high()));
+  if (constdir == 0 && (space[0]->startparam() < -0.5*diag ||
+			space[0]->endparam() > 0.5*diag))
+    {
+      for (int ka=0; ka<2; ++ka)
+	space[ka]->setParamBounds(std::max(space[0]->startparam(), -0.5*diag),
+				  std::min(space[0]->endparam(), 0.5*diag));
+    }
+  
+#ifdef DEBUG_BLEND
+  space[0]->writeStandardHeader(of);
+  space[0]->write(of);
+  space[1]->writeStandardHeader(of);
+  space[1]->write(of);
+#endif
+  RectDomain dom = elem->getParameterBounds();
+  Point parpt1(dom.umin(), dom.vmin()), parpt2(dom.umin(), dom.vmax());
+  Point parpt3(dom.umax(), dom.vmin()), parpt4(dom.umax(), dom.vmax());
+  Point lpos1 = (udir) ? Point(0.0, dom.vmin()) : Point(dom.umin(), 0.0);
+  Point lpos2 = (udir) ? Point(0.0, dom.vmax()) : Point(dom.umax(), 0.0);
+  Point pvec = (udir) ? Point(1.0, 0.0) : Point(0.0, 1.0);
+
+  double t1 = space[0]->startparam();
+  double t2 = space[0]->endparam();
+  shared_ptr<ElementaryCurve> par1(new Line(lpos1, pvec));
+  par1->setParamBounds(t1, t2);
+  shared_ptr<ElementaryCurve> par2(new Line(lpos2, pvec));
+  par2->setParamBounds(t1, t2);
+#ifdef DEBUG_BLEND
+  // Check
+  double tm1 = 0.75*t1 + 0.25*t2;
+  double tm2 = 0.25*t1 + 0.75*t2;
+  Point pp1, pp2, pp3, pp4;
+  Point sp1, sp2, sp3, sp4;
+  Point ssp1, ssp2, ssp3, ssp4;
+  space[0]->point(sp1, tm1);
+  space[0]->point(sp2, tm2);
+  space[1]->point(sp3, tm1);
+  space[1]->point(sp4, tm2);
+  par1->point(pp1, tm1);
+  par1->point(pp2, tm2);
+  par2->point(pp3, tm1);
+  par2->point(pp4, tm2);
+  surf->point(ssp1, pp1[0], pp1[1]);
+  surf->point(ssp2, pp2[0], pp2[1]);
+  surf->point(ssp3, pp3[0], pp3[1]);
+  surf->point(ssp4, pp4[0], pp4[1]);
+#endif
+
+  shared_ptr<ElementaryCurve> adj_par[2];
+  Point close1, close2;
+  double upar1, upar2, vpar1, vpar2, dist1, dist2;
+  Point pos1, pos2;
+  int ix[2];
+  ix[0] = 0;
+  ix[1] = 1;
+  
+  // Check for curve matching
+  space[0]->point(pos1, t1);
+  adj_elem[0]->closestPoint(pos1, upar1, vpar1, close1, dist1, eps);
+  adj_elem[1]->closestPoint(pos1, upar2, vpar2, close2, dist2, eps);
+  if (dist2 < dist1)
+    std::swap(ix[0], ix[1]);
+
+  double tol5 = 5.0*approx_tol_;
+  for (int ka=0; ka<2; ++ka)
+    {
+      adj_par[ix[ka]] = adj_elem[ix[ka]]->getElementaryParamCurve(space[ka].get(),
+								  tol5);
+      if (!adj_par[ix[ka]].get())
+	{
+	  std::cout << "No parameter curve found" << std::endl;
+	}
+
+      // space[ka]->point(pos1, t1);
+      // space[ka]->point(pos2, t2);
+      // adj_elem[ix[ka]]->closestPoint(pos1, upar1, vpar1, close1, dist1, eps);
+      // adj_elem[ix[ka]]->closestPoint(pos2, upar2, vpar2, close2, dist2, eps);
+      // Point pp1(upar1,vpar1), pp2(upar2,vpar2);
+      // Point mpar = (t2*pp1 -t1*pp2)/(t2-t1);
+      // Point mvec = pp2 - pp1;
+      // mvec.normalize();
+
+      // adj_par[ix[ka]] = shared_ptr<ElementaryCurve>(new Line(mpar, mvec));
+      // adj_par[ix[ka]]->setParamBounds(t1, t2);
+    }
+  
+  // Create edges. Orientation in adjacent regions is not set
+  int status = 0;
+  vector<shared_ptr<ftEdge> > bdedg(2);
+  shared_ptr<CurveOnSurface> sfcv1(new CurveOnSurface(elem, par1, space[0], false, 3,
+						      constdir+1, tpar1, 2*constdir, true));
+  bdedg[0] = shared_ptr<ftEdge>(new ftEdge(reg->getSurface(0), sfcv1, t1, t2));
+#ifdef DEBUG_BLEND
+  bool same_orient = sfcv1->sameOrientation();
+  bool same_trace = sfcv1->sameTrace(approx_tol_);
+  bool same_cv = sfcv1->sameCurve(approx_tol_);
+  if ((!same_orient) || (!same_trace) || (!same_cv))
+    std::cout << "Surface curve 1 mismatch " << same_orient << " " << same_trace << " " << same_cv << std::endl;
+#endif
+  shared_ptr<CurveOnSurface> sfcv2(new CurveOnSurface(elem, par2, space[1], false, 3,
+						      constdir+1, tpar2, 2*constdir+1, true));
+  //sfcv2->reverseParameterDirection();
+  bdedg[1] = shared_ptr<ftEdge>(new ftEdge(reg->getSurface(0), sfcv2, t1, t2));
+#ifdef DEBUG_BLEND
+  same_orient = sfcv2->sameOrientation();
+  same_trace = sfcv2->sameTrace(approx_tol_);
+  same_cv = sfcv2->sameCurve(approx_tol_);
+  if ((!same_orient) || (!same_trace) || (!same_cv))
+    std::cout << "Surface curve 2 mismatch " << same_orient << " " << same_trace << " " << same_cv << std::endl;
+#endif
+  reg->addTrimEdge(bdedg[0]);
+  reg->addTrimEdge(bdedg[1]);
+
+  shared_ptr<CurveOnSurface> adj_sfcv[2];
+  shared_ptr<ftEdge> adj_edg[2];
+  vector<RevEngPoint*> out[2];
+  for (int ka=0; ka<2; ++ka)
+    {
+      adj_sfcv[ix[ka]] = shared_ptr<CurveOnSurface>(new CurveOnSurface(adj_elem[ix[ka]],
+								       adj_par[ix[ka]],
+								       space[ka],
+								       false, 1));
+      // if (!adj_sfcv[ix[ka]]->hasParameterCurve())
+      // 	{
+      // 	  vector<shared_ptr<CurveOnSurface> > tmp_cvs;
+      // 	  tmp_cvs.push_back(adj_sfcv[ix[ka]]);
+      // 	  vector<pair<double,double> > t1_t2;
+      // 	  adj[ix[ka]]->getCurveRestriction(tmp_cvs, approx_tol_, anglim_, t1_t2);
+      // 	  if (t1_t2.size() == 1)
+      // 	    {
+      // 	      if (t1_t2[0].first > adj_sfcv[ix[ka]]->startparam() ||
+      // 		  t1_t2[0].second < adj_sfcv[ix[ka]]->endparam())
+      // 		{
+      // 		}
+      // 	    }
+      // 	}
+      adj_edg[ix[ka]] = shared_ptr<ftEdge>(new ftEdge(adj[ix[ka]]->getSurface(0),
+						      adj_sfcv[ix[ka]], t1, t2));
+
+      adj[ix[ka]]->addTrimEdge(adj_edg[ix[ka]]);
+      bdedg[ka]->setReversed(true);
+      adj_edg[ix[ka]]->connectTwin(bdedg[ka].get(), status);
+  
+      // Identify points from the adjacent regions lying outside the corresponding
+      // trimming curve
+      adj[ix[ka]]->extractOutOfEdge(adj_sfcv[ix[ka]],
+				    (ix[ka] == 0) ? intcv1 : intcv2,
+				    rad, approx_tol_, angtol, out[ix[ka]]);
+    }
+
+#ifdef DEBUG_BLEND
+  std::ofstream of2("out_points.g2");
+  for (int ka=0; ka<2; ++ka)
+    {
+      if (out[ka].size() > 0)
+	{
+	  of2 << "400 1 0 4 0 255 0 255" << std::endl;
+	  of2 << out[ka].size() << std::endl;
+	  for (size_t kr=0; kr<out[ka].size(); ++kr)
+	    of2 << out[ka][kr]->getPoint() << std::endl;
+	}
+    }
+#endif
+
+
+  // Add out-points to the blend regions.
+  // NB! This can be too simple in a more complex configuration.
+  // Let's wait for the problem to turn up
+  if (out[0].size() > 0 || out[1].size() > 0)
+    {
+      vector<RevEngPoint*> points;
+      for (int ka=0; ka<2; ++ka)
+	if (out[ka].size() > 0)
+	  points.insert(points.end(), out[ka].begin(), out[ka].end());
+      bool integrated = reg->addPointsToGroup(points, approx_tol_, angtol);
+      if (!integrated)
+	{
+	  vector<HedgeSurface*> dummy_sfs;
+	  vector<RevEngPoint*> dummy_pts;
+	  for (int ka=0; ka<2; ++ka)
+	    {
+	      if (out[ix[ka]].size() > 0)
+		{
+		  vector<vector<RevEngPoint*> > out_1;
+		  adj[ix[ka]]->connectedGroups(out[ix[ka]], out_1, false, dummy_pts);
+		  for (size_t kr=0; kr<regions_.size(); ++kr)
+		    if (regions_[kr].get() == adj[ix[ka]])
+		      {
+			surfaceExtractOutput((int)kr, out_1, dummy_sfs);
+			break;
+		      }
+		}
+	    }
+	}
+    }
+
+  // Identify points associated to the blend region that should be
+  // moved to the adjacent regions
+  vector<int> adj_ix(4);
+  adj_ix[0] = 3;
+  adj_ix[1] = 1;
+  adj_ix[2] = 0;
+  adj_ix[3] = 2;
+  vector<vector<RevEngPoint*> > move2adj(4);
+  vector<RevEngPoint*> remain;
+  vector<RevEngPoint*> regpoints = reg->getPoints();
+  extractOutPoints(regpoints, elem, adj_ix, approx_tol_, angtol,
+		   move2adj, remain);
+
+#ifdef DEBUG_BLEND
+  std::ofstream of2_3("in_points.g2");
+  for (int ka=0; ka<4; ++ka)
+    {
+      if (move2adj[ka].size() > 0)
+	{
+	  of2_3 << "400 1 0 4 100 155 0 255" << std::endl;
+	  of2_3 << move2adj[ka].size() << std::endl;
+	  for (size_t kr=0; kr<move2adj[ka].size(); ++kr)
+	    of2_3 << move2adj[ka][kr]->getPoint() << std::endl;
+	}
+    }
+#endif
+
+  int kx = 2*(1-constdir);
+  for (int ka=kx; ka<=kx+1; ++ka)
+    {
+      if (move2adj[ka].size() > 0)
+	{
+	  remain.insert(remain.end(), move2adj[ka].begin(), move2adj[ka].end());
+	  move2adj[ka].clear();
+	}
+    }
+
+  for (int ka=0; ka<=1; ++ka)
+    {
+      int kb = 2*constdir+ka;
+      if (move2adj[kb].size() > 0)
+	{
+	  reg->removePoints(move2adj[kb]);
+	  bool integrated = adj[ix[ka]]->addPointsToGroup(move2adj[kb],
+							  approx_tol_, angtol);
+	  if (!integrated)
+	    MESSAGE("RevEng::setBlendBoundaris. Missing adjacent surface");
+	}
+    }
+  
+#ifdef DEBUG_BLEND
+  std::ofstream of3("updated_blend.g2");
+  reg->writeRegionPoints(of3);
+  adj[0]->writeRegionPoints(of3);
+  adj[1]->writeRegionPoints(of3);
+#endif
+ int stop_break = 1;
+}
+
+//===========================================================================
+
+// Service functionality for suitcaseCorner
+
+bool getBlendRegMatches(vector<RevEngRegion*>& adj_blends,
+			vector<vector<shared_ptr<ftEdge> > >& trim_edgs,
+			vector<vector<pair<double, double> > >& par_lim,
+			vector<vector<size_t> >& match)
+{
+#ifdef DEBUG_BLEND
+  std::ofstream of("int_pt.g2");
+#endif
+  for (size_t ki=0; ki<adj_blends.size(); ++ki)
+    for (size_t kj=ki+1; kj<adj_blends.size(); ++kj)
+      {
+	size_t kr, kh;
+	for (kr=0; kr<trim_edgs[ki].size(); ++kr)
+	  {
+	    ftEdgeBase *edg1 = trim_edgs[ki][kr]->twin();
+	    ftFaceBase *face1 = (edg1->geomEdge()) ?
+	      edg1->geomEdge()->face() : 0;
+	    if (!face1)
+	      return false;
+	    for (kh=0; kh<trim_edgs[kj].size(); ++kh)
+	      {
+		ftEdgeBase *edg2 = trim_edgs[kj][kh]->twin();
+		ftFaceBase *face2 = (edg2->geomEdge()) ?
+		  edg2->geomEdge()->face() : 0;
+		if (!face2)
+		  return false;
+		if (face1 == face2)
+		  break;
+	      }
+	    if (kh < trim_edgs[kj].size())
+	      break;
+	  }
+	if (kr == trim_edgs[ki].size() || kh == trim_edgs[kj].size())
+	  continue;
+
+	shared_ptr<ParamCurve> cv1 = trim_edgs[ki][kr]->geomCurve();
+	shared_ptr<ParamCurve> cv2 = trim_edgs[kj][kh]->geomCurve();
+	double par1, par2, dist;
+	Point ptc1, ptc2;
+	ClosestPoint::closestPtCurves(cv1.get(), cv2.get(), par1, par2,
+				      dist, ptc1, ptc2);
+#ifdef DEBUG_BLEND
+	of << "400 1 0 4 155 100 0 255" << std::endl;
+	of << "1" << std::endl;
+	of << ptc1 << std::endl;
+	of << "400 1 0 4 155 100 0 255" << std::endl;
+	of << "1" << std::endl;
+	of << ptc2 << std::endl;
+#endif
+	par_lim[ki][kr] = std::make_pair(par1, dist);
+	par_lim[kj][kh] = std::make_pair(par2, dist);
+	vector<size_t> match0{ki, kr, kj, kh};
+	match.push_back(match0);
+      }
+  return true;
+}
+
+
+bool getTrimEdgeMidpoint(vector<vector<shared_ptr<ftEdge> > >& trim_edgs,
+			 vector<vector<pair<double, double> > >& par_lim,
+			 vector<vector<pair<double, double> > >& midp)
+{
+  for (size_t ki=0; ki<par_lim.size(); ++ki)
+    {
+      int num = 0;
+      double par = 0.0;
+      for (size_t kj=0; kj<par_lim[ki].size(); ++kj)
+	if (par_lim[ki][kj].second >= 0.0)
+	  {
+	    par += par_lim[ki][kj].first;
+	    ++num;
+	  }
+      if (num != 2)
+	return false;
+      par /= (double)num;
+      midp[ki].resize(par_lim[ki].size());
+      for (size_t kj=0; kj<par_lim[ki].size(); ++kj)
+	{
+	  if (par_lim[ki][kj].second < 0.0)
+	    {
+	      midp[ki][kj] = std::make_pair(0.0, -1.0);
+	      continue;
+	    }
+	  shared_ptr<ParamCurve> cv = trim_edgs[ki][kj]->geomCurve();
+	  Point pt1 = cv->point(par_lim[ki][kj].first);
+	  Point pt2 = cv->point(par);
+	  double dist = pt1.dist(pt2);
+	  midp[ki][kj] = std::make_pair(par,dist);
+	  //int stop_break = 1;
+	}
+    }
+  return true;
+}
+
+bool computeCornerBoundaryCurves(vector<RevEngRegion*>& adj_blends,
+				 vector<vector<shared_ptr<ftEdge> > >& trim_edgs,
+				 vector<vector<pair<double, double> > >& par_lim,
+				 vector<vector<pair<double, double> > >& midp,
+				 vector<vector<size_t> >& match, double tol,
+				 vector<shared_ptr<CurveOnSurface> >& blend_bd,
+				 vector<RevEngRegion*>& adjreg)
+{
+  double tol1 = 0.5*tol;
+
+  // Want four boundary curves if possible
+  vector<double> midd(midp.size());
+  for (size_t ki=0; ki<midp.size(); ++ki)
+    {
+      double middist = 0.0;
+      for (size_t kj=0; kj<midp[ki].size(); ++kj)
+	middist = std::max(middist, midp[ki][kj].second);
+      midd[ki] = middist;
+    }
+  vector<double> midd2(midd.begin(), midd.end());
+  
+  std::sort(midd2.begin(), midd2.end());
+  double tol2;
+  if (midd2.size() > 4 && midd2.size() < 2)
+    return false;  // Currently not handled
+  else if (midd2.size() == 4)
+    tol2 = 2.0*midd2[3];
+  else if (midd2.size() == 2)
+    tol2 = tol1;
+  else
+    tol2 = 0.5*(midd2[0] + midd2[1]);
+  
+  adjreg.insert(adjreg.end(), adj_blends.begin(), adj_blends.end());
+
+  vector<pair<double,double> > cvpar(adj_blends.size());
+  vector<bool> parset(adj_blends.size(), false);
+
+  // Start defining iso-parametric curves due to close intersection points
+  for (size_t ki=0; ki<adj_blends.size(); ++ki)
+    {
+      if (midd[ki] <= tol1)
+	{
+	  double par;
+	  size_t kj;
+	  for (kj=0; kj<midp[ki].size() && midp[ki][kj].second < 0.0; ++kj);
+	  par = midp[ki][kj].first;
+	  cvpar[ki] = std::make_pair(par, par);
+	  parset[ki] = true;
+	}
+    }
+
+  // Continue with straight curve between opposite intersection points
+  for (size_t ki=0; ki<adj_blends.size(); ++ki)
+    {
+      if (midd[ki] <= tol2)
+	{
+	  double par1, par2;
+	  size_t kj, kr;
+	  for (kj=0; kj<par_lim[ki].size() && par_lim[ki][kj].second < 0.0; ++kj);
+	  par1 = par_lim[ki][kj].first;
+	  for (kr=kj+1; kr<par_lim[ki].size() && par_lim[ki][kr].second < 0.0; ++kr);
+	  par2 = par_lim[ki][kr].first;
+	  cvpar[ki] = std::make_pair(par1, par2);
+	  parset[ki] = true;
+	}
+    }
+
+  if (adj_blends.size() != 3)
+    return false;  // Waiting for a test case
+
+  // Set iso-curve parameters for adjacent blends
+  size_t kj;
+  vector<pair<size_t, size_t> > missing;
+  vector<double> div_par;
+  for (kj=0; kj<parset.size(); ++kj)
+    {
+      if (parset[kj])
+	{
+	  size_t kh, kr;
+	  for (kh=0; kh<par_lim[kj].size() && par_lim[kj][kh].second < 0.0; ++kh);
+	  for (kr=kh+1; kr<par_lim[kj].size() && par_lim[kj][kr].second < 0.0; ++kr);
+
+	  int ix1=-1, ix2=-1;
+	  double par1, par2;
+	  for (size_t kv=0; kv<match.size(); ++kv)
+	    {
+	      if (match[kv][0] == kj)
+		{
+		  if (match[kv][1] == kh)
+		    {
+		      ix1 = (int)match[kv][2];
+		      par1 = par_lim[ix1][match[kv][3]].first;
+		      missing.push_back(std::make_pair(match[kv][2], 1-match[kv][3]));
+		      div_par.push_back(par1);
+		    }		      
+		  else if (match[kv][1] == kr)
+		    {
+		      ix2 = (int)match[kv][2];
+		      par2 = par_lim[ix2][match[kv][3]].first;
+		      missing.push_back(std::make_pair(match[kv][2], 1-match[kv][3]));
+		      div_par.push_back(par2);
+		    }
+		}
+	      else if (match[kv][2] == kj)
+		{
+		  if (match[kv][3] == kh)
+		    {
+		      ix1 = (int)match[kv][0];
+		      par1 = par_lim[ix1][match[kv][1]].first;
+		      missing.push_back(std::make_pair(match[kv][0], 1-match[kv][1]));
+		      div_par.push_back(par1);
+		    }
+		  else if (match[kv][3] == kr)
+		    {
+		      ix2 = (int)match[kv][0];
+		      par2 = par_lim[ix2][match[kv][1]].first;
+		      missing.push_back(std::make_pair(match[kv][0], 1-match[kv][1]));
+		      div_par.push_back(par2);
+		    }
+		}
+	    }
+	  if (ix1 < 0 || ix2 < 0)
+	    return false;
+	  cvpar[ix1] = std::make_pair(par1, par1);
+	  parset[ix1] = true;
+	  cvpar[ix2] = std::make_pair(par2, par2);
+	  parset[ix2] = true;
+	  break;
+	}
+    }
+  if (kj == parset.size())
+    return false;
+  
+  double eps = 1.0e-9;
+  for (size_t ki=0; ki<adj_blends.size(); ++ki)
+    {
+      if (!parset[ki])
+	return false;  // Missing information
+      if (fabs(cvpar[ki].first-cvpar[ki].second) < eps)
+	{
+	  // Iso-parametric curve
+	  size_t kj;
+	  for (kj=0; kj<midp[ki].size() && midp[ki][kj].second < 0.0; ++kj);
+	  double par = 0.5*(cvpar[ki].first+cvpar[ki].second);
+	  shared_ptr<ParamCurve> cv = trim_edgs[ki][kj]->geomCurve();
+	  shared_ptr<CurveOnSurface> sfcv =
+	    dynamic_pointer_cast<CurveOnSurface,ParamCurve>(cv);
+	  shared_ptr<ParamCurve> pcv = sfcv->parameterCurve();
+	  shared_ptr<ParamSurface> surf = sfcv->underlyingSurface();
+	  if ((!pcv.get()) || (!surf.get()))
+	    return false;
+	  double val;
+	  int dir;
+	  bool isconst = sfcv->isConstantCurve(tol1, dir, val);
+	  if (!isconst)
+	    return false;
+	  RectDomain dom = surf->containingDomain();
+	  double pmin = (dir == 1) ? dom.umin() : dom.vmin();
+	  double pmax = (dir == 1) ? dom.umax() : dom.vmax();
+	  shared_ptr<CurveOnSurface> sfcv2(new CurveOnSurface(surf, (3-dir),
+							      par, pmin, pmax,
+							      -1));
+	  blend_bd.push_back(sfcv2);
+	}
+      else
+	{
+	  // Straight curve in the parameter domain
+	  size_t kj, kr;
+	  for (kj=0; kj<par_lim[ki].size() && par_lim[ki][kj].second < 0.0; ++kj);
+	  for (kr=kj+1; kr<par_lim[ki].size() && par_lim[ki][kr].second < 0.0; +kr);
+	  double par1 = cvpar[ki].first;
+	  double par2 = cvpar[ki].second;
+	  shared_ptr<ParamCurve> cv1 = trim_edgs[ki][kj]->geomCurve();
+	  shared_ptr<ParamCurve> cv2 = trim_edgs[ki][kr]->geomCurve();
+	  shared_ptr<CurveOnSurface> sfcv1 =
+			dynamic_pointer_cast<CurveOnSurface,ParamCurve>(cv1);
+	  shared_ptr<CurveOnSurface> sfcv2 =
+			dynamic_pointer_cast<CurveOnSurface,ParamCurve>(cv2);
+	  shared_ptr<ParamCurve> pcv1 = sfcv1->parameterCurve();
+	  shared_ptr<ParamCurve> pcv2 = sfcv2->parameterCurve();
+	  if ((!pcv1.get()) || (!pcv2.get()))
+	    return false;
+	  Point ppt1 = pcv1->point(par1);
+	  Point ppt2 = pcv2->point(par2);
+	  shared_ptr<SplineCurve> pcv3(new SplineCurve(ppt1, ppt2));
+	  shared_ptr<CurveOnSurface> sfcv3(new CurveOnSurface(sfcv1->underlyingSurface(),
+							      pcv3, true));
+	  bool space = sfcv3->ensureSpaceCrvExistence(tol1);
+	  if (!space)
+	    return false;
+	  blend_bd.push_back(sfcv3);
+	}
+    }
+
+#ifdef DEBUG_BLEND
+  std::ofstream of1("space_bd.g2");
+  for (size_t kr=0; kr<blend_bd.size(); ++kr)
+    {
+      shared_ptr<ParamCurve> space = blend_bd[kr]->spaceCurve();
+      shared_ptr<ParamCurve> parcv = blend_bd[kr]->parameterCurve();
+      space->writeStandardHeader(of1);
+      space->write(of1);
+    }
+#endif
+
+  if (missing.size() > 0)
+    {
+      // Add remaining curves
+      size_t ki, kj;
+      for (ki=0; ki<missing.size(); ++ki)
+	for (kj=ki+1; kj<missing.size(); ++kj)
+	  {
+	    size_t kr;
+	    for (kr=0; kr<match.size(); ++kr)
+	      if (missing[ki].first == match[kr][0] &&
+		  missing[ki].second == match[kr][1] &&
+		  missing[kj].first == match[kr][2] &&
+		  missing[kj].second == match[kr][3])
+		break;
+	    if (kr == match.size())
+	      continue; // Should not happen
+	    size_t ki1 = missing[ki].first, kr1 = missing[ki].second;
+	    size_t kj1 = missing[kj].first, kh1 = missing[kj].second;
+	    ftEdgeBase *edg = trim_edgs[ki1][kr1]->twin();
+	    ftSurface *face = edg->geomEdge()->face()->asFtSurface();
+	    if (!face)
+	      continue;
+	    shared_ptr<ParamSurface> surf = face->surface();
+	    shared_ptr<ParamCurve> cv1 = trim_edgs[ki1][kr1]->geomCurve();
+	    shared_ptr<ParamCurve> cv2 = trim_edgs[kj1][kh1]->geomCurve();
+	    vector<Point> der1(2), der2(2);
+	    cv1->point(der1, div_par[ki], 1);
+	    cv2->point(der2, div_par[kj], 1);
+	    double dd = der1[0].dist(der2[0]);
+	    der1[1].normalize();
+	    der2[1].normalize();
+	    if ((der2[0]-der1[0])*der1[1] < 0.0)
+	      der1[1] *= -1;
+	    if ((der2[0]-der1[0])*der2[1] < 0.0)
+	      der2[1] *= -1;
+#ifdef DEBUG_BLEND
+	    std::ofstream of3("missing_cvs.g2");
+	    of3 << "400 1 0 4 255 0 0 255" << std::endl;
+	    of3 << "1" << std::endl;
+	    of3 << der1[0] << std::endl;
+	    of3 << "410 1 0 4 255 0 0 255" << std::endl;
+	    of3 << "1" << std::endl;
+	    of3 << der1[0] << " " << der1[0]+0.3*dd*der1[1] << std::endl;
+	    of3 << "400 1 0 4 255 0 0 255" << std::endl;
+	    of3 << "1" << std::endl;
+	    of3 << der2[0] << std::endl;
+	    of3 << "410 1 0 4 255 0 0 255" << std::endl;
+	    of3 << "1" << std::endl;
+	    of3 << der2[0] << " " << der2[0]+0.3*dd*der2[1] << std::endl;
+#endif
+	    vector<double> knots(8, 0.0);
+	    for (int ka=0; ka<4; ++ka)
+	      knots[4+ka] = dd;
+	    dd /= 3.0;
+	    vector<double> coefs;
+	    coefs.insert(coefs.end(), der1[0].begin(), der1[0].end());
+	    Point cf1 = der1[0] + dd*der1[1];
+	    coefs.insert(coefs.end(), cf1.begin(), cf1.end());
+	    Point cf2 = der2[0] - dd*der2[1];
+	    coefs.insert(coefs.end(), cf2.begin(), cf2.end());
+	    coefs.insert(coefs.end(), der2[0].begin(), der2[0].end());
+	    shared_ptr<ParamCurve> mcv(new SplineCurve(4, 4, &knots[0],
+							&coefs[0], 3));
+#ifdef DEBUG_BLEND
+	    mcv->writeStandardHeader(of3);
+	    mcv->write(of3);
+	    surf->writeStandardHeader(of3);
+	    surf->write(of3);
+#endif
+
+	    shared_ptr<SplineCurve> space_proj, par_proj;
+	    CurveCreators::projectCurve(mcv, surf, tol1, space_proj,
+					par_proj);
+
+	    shared_ptr<CurveOnSurface> sfcv_proj(new CurveOnSurface(surf,
+								    par_proj,
+								    space_proj,
+								    true, 1));
+#ifdef DEBUG_BLEND
+	    space_proj->writeStandardHeader(of3);
+	    space_proj->write(of3);
+#endif
+	    blend_bd.push_back(sfcv_proj);
+	    HedgeSurface *regface =  dynamic_cast<HedgeSurface*>(face);
+	    if (!regface)
+	      return false;
+	    adjreg.push_back(regface->getRegion(0));
+	  }
+    }
+  return true;
+}
+
+bool getCoonsBoundaryInfo(vector<shared_ptr<CurveOnSurface> >& blend_bd,
+			  double tol,
+			  vector<shared_ptr<ParamCurve> >& bdcvs,
+			  vector<shared_ptr<ParamCurve> >& crosscvs)
+{
+  bdcvs.resize(blend_bd.size());
+  crosscvs.resize(blend_bd.size());
+  
+  for (size_t ki=0; ki<blend_bd.size(); ++ki)
+    {
+      shared_ptr<ParamCurve> tmp_cv(blend_bd[ki]->spaceCurve()->clone());
+      if (tmp_cv->instanceType() == Class_Circle)
+	{
+	  shared_ptr<Circle> circ = dynamic_pointer_cast<Circle,ParamCurve>(tmp_cv);
+	  bdcvs[ki] = shared_ptr<SplineCurve>(circ->createNonRationalSpline(tol));
+	}
+      else if (tmp_cv->instanceType() == Class_Line)
+	{
+	  shared_ptr<Line> line = dynamic_pointer_cast<Line,ParamCurve>(tmp_cv);
+	  bdcvs[ki] = shared_ptr<SplineCurve>(line->createSplineCurve());
+	}
+      else if (tmp_cv->instanceType() == Class_SplineCurve)
+	{
+	  shared_ptr<SplineCurve> cv = dynamic_pointer_cast<SplineCurve,ParamCurve>(tmp_cv);
+	  if (cv->rational())
+	    {
+	      shared_ptr<ParamCurve> tmp_par = blend_bd[ki]->parameterCurve();
+	      shared_ptr<ParamSurface> tmp_sf = blend_bd[ki]->underlyingSurface();
+	      bdcvs[ki] =
+		shared_ptr<SplineCurve>(CurveCreators::liftParameterCurve(tmp_par,
+									 tmp_sf,
+									 tol));
+	    }
+	  else
+	    bdcvs[ki] = tmp_cv;
+	}
+      else
+	return false;
+      shared_ptr<CurveOnSurface> tmp_sfcv(blend_bd[ki]->clone());
+      tmp_sfcv->setSpaceCurve(bdcvs[ki]);
+      crosscvs[ki] = CreatorsUtils::createCrossTangent(*tmp_sfcv);
+    }
+
+  // Ensure correct direction of cross tangent curves
+  for (size_t ki=0; ki<crosscvs.size(); ++ki)
+    {
+      size_t kj = (ki == crosscvs.size()-1) ? 0 : ki+1;
+      Point ctan = crosscvs[ki]->point(crosscvs[ki]->endparam());
+      double tdel = bdcvs[kj]->endparam() - bdcvs[kj]->startparam();
+      Point pos1 = bdcvs[kj]->point(bdcvs[kj]->startparam());
+      Point pos2 = bdcvs[kj]->point(bdcvs[kj]->startparam() + 0.1*tdel);
+      Point vec = pos2 - pos1;
+      if (ctan*vec <  0.0)
+	{
+	  shared_ptr<SplineCurve> cross =
+	    dynamic_pointer_cast<SplineCurve,ParamCurve>(crosscvs[ki]);
+	  for (auto it=cross->coefs_begin(); it!=cross->coefs_end(); ++it)
+	    (*it) *= -1.0;
+	}
+    }
+  return true;
+}
+
+void pairOfRegionEdges(RevEngRegion* blendreg, HedgeSurface *hedge,
+		       shared_ptr<ParamCurve>& bdcv1,
+		       shared_ptr<CurveOnSurface>& bdcv2,
+		       RevEngRegion* adjreg, Point& mid, double tol)
+{
+  int stat = 0;
+  double eps = 1.0e-9;
+  shared_ptr<ftEdge> blend_edge(new ftEdge(hedge, bdcv1,
+					   bdcv1->startparam(),
+					   bdcv1->endparam()));
+  blendreg->addTrimEdge(blend_edge);
+
+  int pdir;
+  double pval;
+  if (adjreg->hasBlendEdge() && bdcv2->isConstantCurve(tol, pdir, pval))
+    {
+      shared_ptr<ElementarySurface> adj_elem =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(adjreg->getSurface(0)->surface());
+      if (adj_elem.get())
+	{
+	  RectDomain adjdom = adj_elem->getParameterBounds();
+	  double regdom[4];
+	  adjreg->getDomain(regdom);
+	  // double tmin = regdom[2*(pdir-1)];
+	  // double tmax = regdom[2*(pdir-1)+1];
+	  double upar, vpar, dist;
+	  Point close;
+	  adj_elem->closestPoint(mid, upar, vpar, close, dist, eps);
+	  if (pdir == 1)
+	    {
+	      if (upar > pval)
+		adj_elem->setParameterBounds(adjdom.umin(), adjdom.vmin(),
+					     pval, adjdom.vmax());
+	      else
+		adj_elem->setParameterBounds(pval, adjdom.vmin(),
+					     adjdom.umax(), adjdom.vmax());
+	    }
+	  else
+	    {
+	      if (vpar > pval)
+		adj_elem->setParameterBounds(adjdom.umin(), adjdom.vmin(), 
+					     adjdom.umax(), pval);
+	      else
+		adj_elem->setParameterBounds(adjdom.umin(), pval, 
+					     adjdom.umax(), adjdom.vmax());
+	    }
+	  // if (fabs(tmax-pval) > fabs(pval-tmin))
+	  //   {
+	  //     if (pdir == 1)
+	  // 	adj_elem->setParameterBounds(pval, adjdom.vmin(),
+	  // 				     adjdom.umax(), adjdom.vmax());
+	  //     else
+	  // 	adj_elem->setParameterBounds(adjdom.umin(), pval, 
+	  // 				     adjdom.umax(), adjdom.vmax());
+	  //   }
+	  // else
+	  //   {
+	  //     if (pdir == 1)
+	  // 	adj_elem->setParameterBounds(adjdom.umin(), adjdom.vmin(),
+	  // 				     pval, adjdom.vmax());
+	  //     else
+	  // 	adj_elem->setParameterBounds(adjdom.umin(), adjdom.vmin(), 
+	  // 				     adjdom.umax(), pval);
+	  //   }
+	}
+      adjreg->adaptEdges();
+    }
+
+  HedgeSurface *other_hedge = adjreg->getSurface(0);
+  shared_ptr<ftEdge> other_edge(new ftEdge(other_hedge, bdcv2,
+					   bdcv2->startparam(),
+					   bdcv2->endparam()));
+  adjreg->addTrimEdge(other_edge);
+  other_edge->setReversed(true);
+  blend_edge->connectTwin(other_edge.get(), stat);
+}
+
+//===========================================================================
+void RevEng::extractOutPoints(vector<RevEngPoint*>& points, shared_ptr<ParamSurface> surf,
+			      vector<int>& cv_ix,
+			      double tol, double angtol,
+			      vector<vector<RevEngPoint*> >& move2adj,
+			      vector<RevEngPoint*>& remain)
+//===========================================================================
+{
+  double eps = 1.0e-9;
+  double maxd, avd;
+  int num_in, num2_in;
+  vector<double> parvals;
+  vector<pair<double,double> > dist_ang;
+  vector<RevEngPoint*> inpt, outpt;
+  RevEngUtils::distToSurf(points.begin(), points.end(), surf,
+			  tol, maxd, avd, num_in, num2_in, inpt, outpt,
+			  parvals, dist_ang, angtol);
+
+  RectDomain dom = surf->containingDomain();
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      int bd=-1, bd2=-1;
+      Vector2D par = Vector2D(parvals[2*ki], parvals[2*ki+1]);
+      if (dom.isOnBoundary(par, eps, bd, bd2))
+	{
+	  if (bd2 >= 0)
+	    {
+	      // Check best fit
+	    }
+	  int move_ix = (bd == 0) ? cv_ix[3] :
+	    ((bd == 1) ? cv_ix[1] : ((bd == 2) ? cv_ix[0] : cv_ix[2]));
+	  move2adj[move_ix].push_back(points[ki]);
+	}
+      else
+	remain.push_back(points[ki]);
+    }
+}
+
+//===========================================================================
+bool RevEng::suitcaseCorner(vector<RevEngRegion*>& adj_blends,
+			    RevEngEdge* rev_edge)
+//===========================================================================
+{
+  vector<vector<shared_ptr<ftEdge> > > trim_edgs(adj_blends.size());
+  vector<vector<pair<double, double> > > par_lim(adj_blends.size());
+  for (size_t ki=0; ki<adj_blends.size(); ++ki)
+    {
+      trim_edgs[ki] = adj_blends[ki]->getTrimEdges();
+      par_lim[ki].resize(trim_edgs[ki].size());
+      for (size_t kj=0; kj<par_lim[ki].size(); ++kj)
+	par_lim[ki][kj] = std::make_pair(-1.0, -1.0);   // Dummy
+    }
+
+  vector<vector<size_t> > match;
+  bool found1 = getBlendRegMatches(adj_blends, trim_edgs, par_lim, match);
+  if (!found1)
+    return false;
+
+  vector<vector<pair<double, double> > > midp(par_lim.size());
+  bool found2 = getTrimEdgeMidpoint(trim_edgs, par_lim, midp);
+  if (!found2)
+    return false;
+
+  // Compute boundary curves
+  vector<shared_ptr<CurveOnSurface> > blend_bd;
+  vector<RevEngRegion*> adjreg;
+  bool found3 = computeCornerBoundaryCurves(adj_blends, trim_edgs, par_lim,
+					    midp, match, approx_tol_,
+					    blend_bd, adjreg);
+  if (!found3)
+    return false;
+					    
+
+  // Ensure consistent curve sequence and direction
+  vector<shared_ptr<CurveOnSurface> > blend_bd0(blend_bd.begin(), blend_bd.end());
+  RevEngUtils::setLoopSeq(blend_bd);
+  
+#ifdef DEBUG_BLEND
+  std::ofstream of4("space_bd2.g2");
+  for (size_t kr=0; kr<blend_bd.size(); ++kr)
+    {
+      shared_ptr<ParamCurve> space = blend_bd[kr]->spaceCurve();
+      space->writeStandardHeader(of4);
+      space->write(of4);
+      of4 << "400 1 0 4 255 0 0 255" << std::endl;
+      of4 << "1" << std::endl;
+      of4 << space->point(space->startparam()) << std::endl;
+    }
+#endif
+
+  // Ensure non-rational spline boundary curves and extract cross parameter curves
+  double tol1 = 0.5*approx_tol_;
+  vector<shared_ptr<ParamCurve> > crosscvs;
+  vector<shared_ptr<ParamCurve> > bdcvs;
+  bool found4 = getCoonsBoundaryInfo(blend_bd, tol1, bdcvs, crosscvs);
+  if (!found4)
+    return false;
+
+#ifdef DEBUG_BLEND
+  std::ofstream of4_2("space_bd3.g2");
+  for (size_t kr=0; kr<bdcvs.size(); ++kr)
+    {
+      bdcvs[kr]->writeStandardHeader(of4_2);
+      bdcvs[kr]->write(of4_2);
+    }
+#endif
+  
+  // Create surface
+  shared_ptr<SplineSurface> coons;
+  if (bdcvs.size() == 4)
+    {
+      coons =
+	shared_ptr<SplineSurface>(CoonsPatchGen::createCoonsPatch(bdcvs,
+								  crosscvs,
+								  tol1,
+								  anglim_));
+      RevEngUtils::smoothSurf(coons, 2);
+    }
+  else
+    {
+      MESSAGE("RevEng::suitcaseCorner. Only 4 boundary curves are supported.");
+      return false;
+    }
+
+  if (!coons.get())
+    return false;
+  
+#ifdef DEBUG_BLEND
+  std::ofstream of5("coons_patch.g2");
+  coons->writeStandardHeader(of5);
+  coons->write(of5);
+#endif
+
+  // Set trimming curves
+  double eps = 1.0e-9;
+  CurveLoop cvloop = SurfaceTools::outerBoundarySfLoop(coons, eps);
+  vector<shared_ptr<ParamCurve> > loopcvs = cvloop.getCurves();
+
+  // Define match between boundary curves of corner surface and
+  // adjacent surfaces
+  vector<int> cv_ix(loopcvs.size());
+  for (size_t ki=0; ki<loopcvs.size(); ++ki)
+    {
+      Point mid = loopcvs[ki]->point(0.5*(loopcvs[ki]->startparam()+
+					  loopcvs[ki]->endparam()));
+      int ix = -1;
+      double mindist = std::numeric_limits<double>::max();
+      for (size_t kj=0; kj<blend_bd0.size(); ++kj)
+	{
+	  double tpar, dist;
+	  Point close;
+	  blend_bd0[kj]->closestPoint(mid, blend_bd0[kj]->startparam(),
+				     blend_bd0[kj]->endparam(), tpar,
+				     close, dist);
+	  if (dist < mindist)
+	    {
+	      ix = (int)kj;
+	      mindist = dist;
+	    }
+	}
+      cv_ix[ki] = ix;
+    }
+
+  // Move points from blend region as appropriate. First fetch blend points
+    vector<RevEngRegion*> blend_regs;
+  rev_edge->getAllBlendRegs(blend_regs);
+  vector<RevEngPoint*> blend_pts;
+  for (size_t ki=0; ki<blend_regs.size(); ++ki)
+    blend_pts.insert(blend_pts.end(), blend_regs[ki]->pointsBegin(),
+		     blend_regs[ki]->pointsEnd());
+
+  // double angtol = 5.0*anglim_;
+  // double maxd, avd;
+  // int num_in, num2_in;
+  // vector<double> parvals;
+  // vector<pair<double,double> > dist_ang;
+  // vector<RevEngPoint*> inpt, outpt;
+  // RevEngUtils::distToSurf(blend_pts.begin(), blend_pts.end(), coons,
+  // 			  approx_tol_, maxd, avd, num_in, num2_in, inpt, outpt,
+  // 			  parvals, dist_ang, angtol);
+
+  double angtol = 5.0*anglim_;
+  vector<vector<RevEngPoint*> > move2adj(4);
+  vector<RevEngPoint*> remain;
+  extractOutPoints(blend_pts, coons, cv_ix, approx_tol_, angtol,
+		   move2adj, remain);
+  // RectDomain dom = coons->containingDomain();
+  // for (size_t ki=0; ki<blend_pts.size(); ++ki)
+  //   {
+  //     int bd=-1, bd2=-1;
+  //     Vector2D par = Vector2D(parvals[2*ki], parvals[2*ki+1]);
+  //     if (dom.isOnBoundary(par, eps, bd, bd2))
+  // 	{
+  // 	  if (bd2 >= 0)
+  // 	    {
+  // 	      // Check best fit
+  // 	    }
+  // 	  int move_ix = (bd == 0) ? cv_ix[3] :
+  // 	    ((bd == 1) ? cv_ix[1] : ((bd == 2) ? cv_ix[0] : cv_ix[2]));
+  // 	  move2adj[move_ix].push_back(blend_pts[ki]);
+  // 	}
+  //     else
+  // 	remain.push_back(blend_pts[ki]);
+  //   }
+  // if (remain.size() == 0 && remain.size() < blend_pts.size())
+  //   return false;
+
+  bool OK;
+  for (size_t ki=0; ki<move2adj.size(); ++ki)
+    if (move2adj[ki].size() > 0)
+      OK = adjreg[ki]->addPointsToGroup(move2adj[ki], approx_tol_, angtol);
+
+  // Delete current blend regions
+  vector<HedgeSurface*> prev_sfs;
+  for (size_t ki=0; ki<blend_regs.size(); ++ki)
+    {
+      //blend_regs[ki]->removeFromAdjacent();
+      blend_regs[ki]->setRemove();
+    }
+  
+  // Create blend region
+  shared_ptr<RevEngRegion> blendreg(new RevEngRegion(classification_type_,
+						     edge_class_type_,
+						     remain));  
+  blendreg->setRegionAdjacency();
+  regions_.push_back(blendreg);
+  shared_ptr<HedgeSurface> hedge;
+  shared_ptr<ParamSurface> blend_tmp = coons;
+  blendreg->setAssociatedSurface(blend_tmp, approx_tol_, angtol,
+				 min_point_region_, hedge);
+  rev_edge->setBlendRegSurf(blendreg.get());
+  blendreg->setBlendEdge(rev_edge);
+  if (hedge.get())
+    surfaces_.push_back(hedge);
+  
+  // Add edges to regions
+  RectDomain dom = coons->containingDomain();
+  double umid = 0.5*(dom.umin()+dom.umax());
+  double vmid = 0.5*(dom.vmin()+dom.vmax());
+  Point mid = coons->ParamSurface::point(umid, vmid);
+  for (size_t ki=0; ki<loopcvs.size(); ++ki)
+    {
+      shared_ptr<CurveOnSurface> other = blend_bd0[cv_ix[ki]];
+      RevEngRegion *adj = adjreg[cv_ix[ki]];
+      pairOfRegionEdges(blendreg.get(), hedge.get(), loopcvs[ki], other, adj, mid,
+			tol1);
+    }
+
+  // Move points from adjacent regions to corner blend region as appropriate
+  for (size_t ki=0; ki<adjreg.size(); ++ki)
+    {
+      blendreg->growInDomain(adjreg[ki], approx_tol_, angtol);
+      if (adjreg[ki]->numPoints() == 0)
+	adjreg[ki]->setRemove();
+    }
+  
+  // Remove obsolete edge information
+  RevEngRegion* adj[2];
+  rev_edge->getAdjacent(adj[0], adj[1]);
+  size_t kr;
+  for (int ka=0; ka<2; ++ka)
+    {
+      for (kr=0; kr<adj_blends.size(); ++kr)
+  	if (adj_blends[kr] == adj[ka])
+  	  break;
+      if (kr == adj_blends.size())
+  	{
+  	  rev_edge->eraseAdjacent(adj[ka]);
+  	  break;
+  	}
+    }
+  rev_edge->eraseCurves();
+  
+  return true;
+}
+
+//===========================================================================
+bool RevEng::createBlendSurface(int ix)
+//===========================================================================
+{
+  double angtol = 5.0*anglim_;
+  double diag = bbox_.low().dist(bbox_.high());
+  double eps = 1.0e-6;
+  double blendlim = std::min(0.1*diag, 30.0*mean_edge_len_); //50.0*mean_edge_len_;
+  double lenlim = 10.0*mean_edge_len_; //blendlim;
+  
+  // Adjacent regions
+  RevEngRegion* adj[2];
+  edges_[ix]->getAdjacent(adj[0], adj[1]);
+
+  if ((!adj[0]) || (!adj[1]))
+    return false;
+  if ((!adj[0]->hasSurface()) || (!adj[1]->hasSurface()))
+    return false;  // Something wrong
+
+#ifdef DEBUG_BLEND
+  std::ofstream of1("adj_groups.g2");
+  adj[0]->writeRegionPoints(of1);
+  adj[1]->writeRegionPoints(of1);
+#endif
+
+  if (adj[0]->hasAssociatedBlend() || adj[1]->hasAssociatedBlend())
+    return false;
+
+  // Update intersection curve if necessary
+  bool updated_edge = false;
+  if (edges_[ix]->getExtendCount() > 0)
+    {
+      vector<shared_ptr<RevEngRegion> > added_regions;
+      vector<vector<RevEngPoint*> > extract_groups;
+      vector<HedgeSurface*> out_sfs;
+      updated_edge = edges_[ix]->extendCurve(int_tol_, approx_tol_, anglim_, diag, lenlim,
+					    blendlim, added_regions, extract_groups, out_sfs);
+      if (extract_groups.size() > 0 || out_sfs.size() > 0)
+	surfaceExtractOutput(-1, extract_groups, out_sfs);
+      for (size_t kj=0; kj<added_regions.size(); ++kj)
+	{
+	  added_regions[kj]->setRegionAdjacency();
+	  regions_.push_back(added_regions[kj]);
+	}
+
+      if (updated_edge)
+	{
+	  for (int ka=ix+1; ka<(int)edges_.size(); ++ka)
+	    {
+	      // Check for overlap (simplified version)
+	      bool embedded = edges_[ix]->contains(edges_[ka].get(), approx_tol_);
+	      if (embedded)
+		{
+		  bool done = edges_[ix]->integrate(edges_[ka].get());
+		  if (done)
+		    edges_.erase(edges_.begin()+ka);
+		}
+	    }
+	}
+    }
+
+  if (updated_edge == false && edges_[ix]->getSurfChangeCount() > 0)
+    updated_edge = edges_[ix]->updateCurve(int_tol_, approx_tol_, diag);
+  if (!updated_edge)
+    edges_[ix]->fixMismatchCurves(approx_tol_);  // @@@ Should this be done always?
+
+  if (edges_[ix]->getType() == NOT_BLEND)
+    return false;  // Should not create blend surfaces
+  
+  // Regions in blend area
+  vector<RevEngRegion*> blend_regs;
+  edges_[ix]->getAllBlendRegs(blend_regs);
+  
+  // Intersection curve
+  vector<shared_ptr<CurveOnSurface> > cvs;
+  edges_[ix]->getCurve(cvs, true);
+
+  vector<Point> der(2);
+  cvs[0]->point(der, 0.5*(cvs[0]->startparam()+cvs[0]->endparam()), 1);
+
+#ifdef DEBUG_BLEND
+  for (size_t ki=0; ki<cvs.size(); ++ki)
+    {
+      shared_ptr<ParamCurve> tmp_cv = cvs[ki]->spaceCurve();
+      tmp_cv->writeStandardHeader(of1);
+      tmp_cv->write(of1);
+    }
+#endif
+
+  // Width
+  double width = edges_[ix]->getDistance();
+
+  bool out1 = false, out2 = false;
+  edges_[ix]->getOuterInfo(out1, out2);
+
+  shared_ptr<ParamSurface> surf1 = adj[0]->getSurface(0)->surface();
+  shared_ptr<ParamSurface> surf2 = adj[1]->getSurface(0)->surface();
+  ClassType classtype1 = surf1->instanceType();
+  ClassType classtype2 = surf2->instanceType();
+  if (classtype1 != Class_Plane && classtype1 != Class_Cylinder &&
+      classtype1 != Class_Cone)
+    return false;
+  if (classtype2 != Class_Plane && classtype2 != Class_Cylinder &&
+      classtype2 != Class_Cone)
+    return false;
+  shared_ptr<ElementarySurface> elem1 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+  shared_ptr<ElementarySurface> elem2 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+  vector<shared_ptr<ElementarySurface> > elem_sfs(2);
+  elem_sfs[0] = elem1;
+  elem_sfs[1] = elem2;
+  if (!(elem1.get() && elem2.get()))
+    return false;
+  Point dir1 = elem1->direction();
+  Point norm1 = adj[0]->getMeanNormalTriang();
+  if (elem1->instanceType() == Class_Plane && dir1*norm1 < 0.0)
+    dir1 *= -1;
+  Point dir2 = elem2->direction();
+  Point norm2 = adj[1]->getMeanNormalTriang();
+  if (elem2->instanceType() == Class_Plane && dir2*norm2 < 0.0)
+    dir2 *= -1;
+
+  if (classtype1 == Class_Plane && classtype2 == Class_Plane)
+    {
+      // Create cylinder
+    }
+  else if (classtype1 == Class_Plane || classtype2 == Class_Plane)
+    {
+      // Create torus
+    }
+  else if ((classtype1 == Class_Cylinder && classtype2 == Class_Cone) ||
+	   (classtype2 == Class_Cylinder && classtype1 == Class_Cone))
+    {
+      // Create torus
+    }
+  else
+    return false;  // Not supported
+
+  // Collect points from adjacent surfaces
+  vector<vector<RevEngPoint*> > blend_pts(3);
+  double tmin = cvs[0]->startparam();
+  double tmax = cvs[0]->endparam();
+  for (int ka=0; ka<2; ++ka)
+    adj[ka]->getNearPoints(cvs[0], tmin, tmax, 2*width, angtol, blend_pts[ka]);
+ 
+  // Collect points from associated blend regions
+  for (size_t ki=0; ki<blend_regs.size(); ++ki)
+    blend_pts[2].insert(blend_pts[2].end(), blend_regs[ki]->pointsBegin(),
+			blend_regs[ki]->pointsEnd());
+
+#ifdef DEBUG_BLEND
+  std::ofstream of2("blend_pts.g2");
+  for (size_t ki=0; ki<3; ++ki)
+    {
+      if (blend_pts[ki].size() > 0)
+	{
+	  of2 << "400 1 0 4 0 255 0 255" << std::endl;
+	  of2 << blend_pts[ki].size() << std::endl;
+	  for (size_t kr=0; kr<blend_pts[ki].size(); ++kr)
+	    of2 << blend_pts[ki][kr]->getPoint() << std::endl;
+	}
+    }
+#endif
+
+  shared_ptr<ElementarySurface> blend_surf;
+  double ylen = 0.0;
+  double axis_ang = dir1.angle(dir2);
+  int ldir = -1;
+  if ((classtype1 == Class_Plane && classtype2 == Class_Plane) ||
+      ((classtype1 == Class_Plane || classtype2 == Class_Plane) &&
+       fabs(0.5*M_PI - axis_ang) <= angtol))
+    {
+      // Create cylinder
+      Point lin1, lin2;
+      Point dir1_2 = dir1, dir2_2 = dir2;
+      if (classtype1 == Class_Plane)
+	lin1 = der[1].cross(dir1);
+      else
+	{
+	  double clo_u, clo_v, clo_dist;
+	  Point clo;
+	  surf1->closestPoint(der[0], clo_u, clo_v, clo, clo_dist, eps);
+	  surf1->normal(dir1_2, clo_u, clo_v);
+	  lin1 = der[1].cross(dir1_2);
+	}
+      
+      if (classtype2 == Class_Plane)
+	lin2 = der[1].cross(dir2);
+      else
+	{
+	  double clo_u, clo_v, clo_dist;
+	  Point clo;
+	  surf2->closestPoint(der[0], clo_u, clo_v, clo, clo_dist, eps);
+	  surf2->normal(dir2_2, clo_u, clo_v);
+	  lin2 = der[1].cross(dir2_2);
+	}
+      
+      lin1.normalize();
+      if (lin1*dir2_2 < 0.0)
+	lin1 *= -1;
+      lin2.normalize();
+      if (lin2*dir1_2 < 0.0)
+	lin2 *= -1;
+      int sgn = (out1 && out2) ? 1 : -1;
+      blend_surf = createCylinderBlend(blend_pts, width, der[0],
+				       der[1], lin1, lin2, sgn);
+      double radius = blend_surf->radius(0.0, 0.0);
+      //double ang = dir1.angle(dir2);
+      Point centre = blend_surf->location();
+      double xlen = der[0].dist(centre);
+      ylen = sqrt(xlen*xlen - radius*radius);
+      
+      ldir = 1;
+    }
+  else
+    {
+      int sgn = 1;
+      if ((classtype1 == Class_Plane && dir1*elem1->direction() < 0.0) ||
+	  (classtype2 == Class_Plane && dir2*elem2->direction() < 0.0))
+	sgn = -1;
+
+      blend_surf = torusBlend(blend_pts, cvs[0], elem1, elem2, width,
+			      out1, out2, sgn);
+
+      double Rrad = blend_surf->radius(0.0, 0.0);
+      Point centre = blend_surf->location();
+      double xlen = der[0].dist(centre);
+      ylen = fabs(xlen - Rrad);
+
+      ldir = 1;
+    }
+  
+  if (!blend_surf.get())
+    {
+#ifdef DEBUG_BLEND
+      std::cout << "No blend_surf" << std::endl;
+#endif
+      return false;
+    }
+
+#ifdef DEBUG_BLEND
+  for (int ka=0; ka<2; ++ka)
+    {
+      int num = adj[ka]->numPoints();
+      for (int kb=0; kb<num; ++kb)
+	if (adj[ka]->getPoint(kb)->region() != adj[ka])
+	  std::cout << "Inconsistent region pointers, pre sortBlendPoints: " << ka << " " << kb << std::endl;
+    }
+#endif
+  
+  // Associate blend points with the appropriate surface (region)
+  // First remove blend points from the adjacent surfaces
+  vector<vector<RevEngPoint*> > out_pts(2);
+  for (int ka=0; ka<2; ++ka)
+    {
+      adj[ka]->sortBlendPoints(blend_pts[ka], cvs, ylen, true, out_pts[ka]);
+      blend_pts[2].insert(blend_pts[2].end(), out_pts[ka].begin(),
+			  out_pts[ka].end());
+    }
+#ifdef DEBUG_BLEND
+  for (int ka=0; ka<2; ++ka)
+    {
+      int num = adj[ka]->numPoints();
+      for (int kb=0; kb<num; ++kb)
+	if (adj[ka]->getPoint(kb)->region() != adj[ka])
+	  std::cout << "Inconsistent region pointers, post sortBlendPoints1: " << ka << " " << kb << std::endl;
+    }
+#endif
+      
+  // Move blend points to the adjacent surfaces if appropriate
+  vector<vector<RevEngPoint*> > in_pts(2);
+  adj[0]->sortBlendPoints(blend_pts[2], cvs, ylen, adj[1],
+			  in_pts[0], in_pts[1]);
+
+#ifdef DEBUG_BLEND
+  for (int ka=0; ka<2; ++ka)
+    {
+      int num = adj[ka]->numPoints();
+      for (int kb=0; kb<num; ++kb)
+	if (adj[ka]->getPoint(kb)->region() != adj[ka])
+	  std::cout << "Inconsistent region pointers, post sortBlendPoints2: " << ka << " " << kb << std::endl;
+    }
+#endif
+      
+  // Update adjacent surfaces with modified collection of points
+  for (int ka=0; ka<2; ++ka)
+    {
+      adj[ka]->updateWithPointsInOut(out_pts[ka], in_pts[ka], approx_tol_, angtol);
+    }
+
+  // Delete current blend regions
+  vector<HedgeSurface*> prev_sfs;
+  for (size_t ki=0; ki<blend_regs.size(); ++ki)
+    {
+      blend_regs[ki]->removeFromAdjacent();
+      blend_regs[ki]->clearRegionAdjacency();
+      int num_sfs = blend_regs[ki]->numSurface();
+      for (int ka=0; ka<num_sfs; ++ka)
+	prev_sfs.push_back(blend_regs[ki]->getSurface(ka));
+    }
+  edges_[ix]->clearBlendRegions();
+  if (blend_regs.size() > 0)
+    {
+      int dummy_ix = -1;
+      updateRegionsAndSurfaces(dummy_ix, blend_regs, prev_sfs);
+    }
+  
+  if (blend_pts[2].size() == 0)
+    {
+#ifdef DEBUG_BLEND
+      std::cout << "No points for blend_surf" << std::endl;
+#endif
+      return false;
+    }
+  
+  // Define blend region
+  shared_ptr<RevEngRegion> blendreg(new RevEngRegion(classification_type_,
+						     edge_class_type_,
+						     blend_pts[2]));
+#ifdef DEBUG_BLEND
+  std::ofstream of3("updated_blend_pts.g2");
+  adj[0]->writeRegionPoints(of3);
+  adj[1]->writeRegionPoints(of3);
+  blendreg->writeRegionPoints(of3);
+#endif
+  
+  blendreg->setRegionAdjacency();
+  regions_.push_back(blendreg);
+  shared_ptr<HedgeSurface> hedge;
+  shared_ptr<ParamSurface> blend_surf_tmp = blend_surf;
+  blendreg->setAssociatedSurface(blend_surf_tmp, approx_tol_, angtol,
+				 min_point_region_, hedge);
+  if (hedge.get())
+    surfaces_.push_back(hedge);
+
+  // // Check blend points
+  // vector<vector<RevEngPoint*> > out_groups;
+  // blendreg->extractOutPoints(ldir, approx_tol_, angtol,
+  // 			     1.1*angtol, out_groups);
+  // if (out_groups.size() > 0)
+  //   {
+  //     vector<HedgeSurface*> dummy_sfs;
+  //     surfaceExtractOutput(regions_.size()-1, out_groups, dummy_sfs);
+  //   }
+   
+  // Update edge with blend region (surface)
+  edges_[ix]->setBlendRegSurf(blendreg.get());
+  blendreg->setBlendEdge(edges_[ix].get());
+  for (int ka=0; ka<2; ++ka)
+    adj[ka]->updateRegionAdjacency();
+  blendreg->setRegionAdjacency();
+  //edges_[ix]->setAltRadius(radius1);
+
+  for (int ka=0; ka<2; ++ka)
+    {
+      vector<vector<RevEngPoint*> > sep_groups;
+      adj[ka]->splitRegion(sep_groups);
+      if (sep_groups.size() > 0)
+	{
+	  size_t kh;
+	  for (kh=0; kh<regions_.size(); ++kh)
+	    if (regions_[kh].get() == adj[ka])
+	      break;
+	  vector<HedgeSurface*> dummy_sfs;
+	  surfaceExtractOutput((kh<regions_.size()) ? (int)kh : 0, sep_groups,
+			       dummy_sfs);
+	}
+   }
+
+  return true;
+}
+
+//===========================================================================
+double
+RevEng::computeTorusRadius(vector<vector<RevEngPoint*> >& blend_pts,
+			   shared_ptr<CurveOnSurface>& cv,
+			   const Point& locp, const Point& normal,
+			   shared_ptr<ElementarySurface> rotational,
+			   double width, bool plane_out, bool rot_out)
+//===========================================================================
+{
+  double alpha = 0;
+  shared_ptr<Cone> cone = dynamic_pointer_cast<Cone,ElementarySurface>(rotational);
+  if (cone.get())
+    alpha = cone->getConeAngle();
+  Point axis = rotational->direction();
+  
+  // Compute minor radius of torus
+  double lrad = 0.0;
+  int lnmb = 0;
+  double beta = 0.5*M_PI + alpha;
+  //double phi = 0.5*M_PI - alpha;
+  double fac = 1.0/sin(0.5*beta);
+  double div = fac - 1.0;
+  Point loc = rotational->location();
+  double rd = (locp-loc)*axis;
+  Point centr = loc + rd*axis;
+  for (size_t kj=0; kj<blend_pts.size(); ++kj)
+    {
+      for (size_t ki=0; ki<blend_pts[kj].size(); ++ki)
+	{
+	  Vector3D xyz = blend_pts[kj][ki]->getPoint();
+	  Point ptpos(xyz[0], xyz[1], xyz[2]);
+
+	  // Localize with respect to the guiding curve(s)
+	  double tpar, dist;
+	  Point close;
+	  cv->closestPoint(ptpos, cv->startparam(), cv->endparam(),
+				tpar, close, dist);
+
+	  // Define line in the point between the adjacent surfaces
+	  vector<Point> der(2);
+	  cv->point(der, tpar, 1);
+	  der[1].normalize();
+	  Point dir1 = der[1].cross(normal);
+	  Point vec1 = der[0] - centr;
+	  if ((dir1*vec1 < 0.0 && (!rot_out)) || (dir1*vec1 > 0.0 && rot_out))
+	    dir1 *= -1;
+	  Point dir2 = (plane_out) ? normal : -normal;
+	  Point dir3;
+	  if (alpha > 0)
+	    {
+	      Matrix3D mat;
+	      mat.setToRotation(-alpha, der[1][0], der[1][1], der[1][2]);
+	      Vector3D dir2_2(dir2[0], dir2[1], dir2[2]);
+	      Vector3D dir3_2 = mat*dir2_2;
+	      dir3 = Point(dir3_2[0], dir3_2[1], dir3_2[2]);
+	    }
+	  else
+	    dir3 = dir2;
+	  Point dir4 = 0.5*(dir1 + dir3);
+#ifdef DEBUG_BLEND
+	  std::ofstream of("midlin.g2");
+	  of << "410 1 0 4 0 255 0 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir1 << std::endl;
+	  of << "410 1 0 4 255 0 0 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir4 << std::endl;
+	  of << "410 1 0 4 0 0 255 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir2 << std::endl;
+	      
+#endif
+	  shared_ptr<Line> line(new Line(der[0], dir4));
+	  line->setParameterInterval(-2*width, 2*width);
+	  double lpar, ldist;
+	  Point lclose;
+	  line->closestPoint(ptpos, line->startparam(), line->endparam(),
+			     lpar, lclose, ldist);
+	  if (ldist <= approx_tol_)
+	    {
+	      double dd2 = close.dist(lclose);
+	      double xlen = dd2/div;
+	      lrad += xlen;
+	      lnmb++;
+	    }
+	  int stop_break = 1;
+	}
+    }
+
+  if (lnmb > 0)
+    lrad /= (double)lnmb;
+
+  return lrad;
+}
+
+//===========================================================================
+void RevEng::getTorusParameters(shared_ptr<ElementarySurface> planar,
+				shared_ptr<ElementarySurface> rotational,
+				double radius, int sgn1, int sgn2, double& Rrad, 
+				Point& centre, Point& normal, Point& Cx)
+//===========================================================================
+{
+  Point locp = planar->location();
+  normal = planar->direction();
+  Point loc = rotational->location();
+  Point axis = rotational->direction();
+  double rd = (locp - loc)*axis;
+  Point centre0 = loc + rd*axis;
+  centre = centre0 - sgn1*radius*normal;
+  Rrad = rotational->radius(0.0, rd);
+  double alpha = 0.0;
+  if (rotational->instanceType() == Class_Cone)
+    alpha = ((Cone*)(rotational.get()))->getConeAngle();
+  double phi = 0.5*M_PI - alpha;
+  double sd = radius/sin(phi);
+  Cx = rotational->direction2();
+  Rrad += (sgn2*sd);
+}
+
+//===========================================================================
+shared_ptr<Torus>
+RevEng::torusBlend(vector<vector<RevEngPoint*> >& blend_pts,
+		   vector<shared_ptr<CurveOnSurface> >& cvs,
+		   const Point& locp, const Point& normal,
+		   shared_ptr<ElementarySurface> rotational,
+		   double width, bool plane_out, bool rot_out)
+//===========================================================================
+{
+  shared_ptr<Torus> torus;
+
+  double alpha = 0;
+  shared_ptr<Cone> cone = dynamic_pointer_cast<Cone,ElementarySurface>(rotational);
+  if (cone.get())
+    alpha = cone->getConeAngle();
+  Point axis = rotational->direction();
+  
+  // Compute minor radius of torus
+  double lrad = 0.0;
+  double d2 = 0.0;
+  int lnmb = 0;
+  double beta = 0.5*M_PI + alpha;
+  double phi = 0.5*M_PI - alpha;
+  double fac = 1.0/sin(0.5*beta);
+  double div = fac - 1.0;
+  Point loc = rotational->location();
+  double rd = (locp-loc)*axis;
+  Point centr = loc + rd*axis;
+  for (size_t kj=0; kj<blend_pts.size(); ++kj)
+    {
+      for (size_t ki=0; ki<blend_pts[kj].size(); ++ki)
+	{
+	  Vector3D xyz = blend_pts[kj][ki]->getPoint();
+	  Point ptpos(xyz[0], xyz[1], xyz[2]);
+
+	  // Localize with respect to the guiding curve(s)
+	  double tpar, dist=std::numeric_limits<double>::max();
+	  Point close;
+	  int ix = -1;
+	  for (size_t kr=0; kr<cvs.size(); ++kr)
+	    {
+	      double tpar0, dist0;
+	      Point close0;
+	      cvs[kr]->closestPoint(ptpos, cvs[kr]->startparam(), cvs[kr]->endparam(),
+				tpar0, close0, dist0);
+	      if (dist0 < dist)
+		{
+		  tpar = tpar0;
+		  dist = dist0;
+		  close = close0;
+		  ix = (int)kr;
+		}
+	      if (ix < 0)
+		continue;
+
+	      // Define line in the point between the adjacent surfaces
+	      vector<Point> der(2);
+	      cvs[ix]->point(der, tpar, 1);
+	      der[1].normalize();
+	      Point dir1 = der[1].cross(normal);
+	      Point vec1 = der[0] - centr;
+	      if ((dir1*vec1 < 0.0 && (!rot_out)) || (dir1*vec1 > 0.0 && rot_out))
+		dir1 *= -1;
+	      Point dir2 = (plane_out) ? normal : -normal;
+	      Point dir3;
+	      if (alpha > 0)
+		{
+		  Matrix3D mat;
+		  mat.setToRotation(-alpha, der[1][0], der[1][1], der[1][2]);
+		  Vector3D dir2_2(dir2[0], dir2[1], dir2[2]);
+		  Vector3D dir3_2 = mat*dir2_2;
+		  dir3 = Point(dir3_2[0], dir3_2[1], dir3_2[2]);
+		}
+	      else
+		dir3 = dir2;
+	      Point dir4 = 0.5*(dir1 + dir3);
+#ifdef DEBUG_BLEND
+	      std::ofstream of("midlin.g2");
+	      of << "410 1 0 4 0 255 0 255" << std::endl;
+	      of << "1" << std::endl;
+	      of << der[0] << " " << der[0]+dir1 << std::endl;
+	      of << "410 1 0 4 255 0 0 255" << std::endl;
+	      of << "1" << std::endl;
+	      of << der[0] << " " << der[0]+dir4 << std::endl;
+	      of << "410 1 0 4 0 0 255 255" << std::endl;
+	      of << "1" << std::endl;
+	      of << der[0] << " " << der[0]+dir2 << std::endl;
+	      
+#endif
+	      shared_ptr<Line> line(new Line(der[0], dir4));
+	      line->setParameterInterval(-2*width, 2*width);
+	      double lpar, ldist;
+	      Point lclose;
+	      line->closestPoint(ptpos, line->startparam(), line->endparam(),
+				 lpar, lclose, ldist);
+	      if (ldist <= approx_tol_)
+		{
+		  double dd2 = close.dist(lclose);
+		  double xlen = dd2/div;
+		  lrad += xlen;
+		  d2 += dd2;
+		  lnmb++;
+		}
+	      int stop_break = 1;
+	    }
+	}
+    }
+
+  if (lnmb > 0)
+    {
+      lrad /= (double)lnmb;
+      d2 /= (double)lnmb;
+    }
+
+  int sgn1 = (plane_out) ? -1 : 1;
+  int sgn2 = rot_out ? -1 : 1;
+  Point pos = centr - sgn1*lrad*normal;
+  double Rrad = rotational->radius(0.0, rd);
+  double sd = lrad/sin(phi);
+  Point Cx = rotational->direction2();
+  torus = shared_ptr<Torus>(new Torus(Rrad+sgn2*sd, lrad, pos, normal, Cx));
+  if (rot_out)
+    {
+      RectDomain dom = torus->getParameterBounds();
+      try {
+	torus->setParameterBounds(dom.umin(), dom.vmin()-M_PI,
+				  dom.umax(), dom.vmax()-M_PI);
+      }
+      catch (...)
+	{
+	}
+    }
+  
+#ifdef DEBUG_BLEND
+  std::ofstream of2("tor_blend.g2");
+  torus->writeStandardHeader(of2);
+  torus->write(of2);
+  RectDomain dom2 = torus->getParameterBounds();
+  vector<shared_ptr<ParamCurve> > tmp_cvs = torus->constParamCurves(dom2.vmin(), true);
+  tmp_cvs[0]->writeStandardHeader(of2);
+  tmp_cvs[0]->write(of2);
+  double tf = (sgn2 == 1) ? 0.5 : 1.5;
+  vector<shared_ptr<ParamCurve> > tmp_cvs2 =
+    torus->constParamCurves(dom2.vmin()+tf*M_PI, true);
+  tmp_cvs2[0]->writeStandardHeader(of2);
+  tmp_cvs2[0]->write(of2);
+#endif
+  return torus;
+}
+
+//===========================================================================
+double
+RevEng::computeTorusRadius(vector<vector<RevEngPoint*> >& blend_pts,
+			   shared_ptr<CurveOnSurface>& cv,
+			   shared_ptr<ElementarySurface> elem1,
+			   shared_ptr<ElementarySurface> elem2,
+			   double width, bool out1, bool out2, int sgn,
+			   double& d2)
+//===========================================================================
+{
+  d2 = 0.0;
+  double alpha1 = 0.0, alpha2 = 0.0;
+  shared_ptr<Cone> cone1 = dynamic_pointer_cast<Cone,ElementarySurface>(elem1);
+  if (cone1.get())
+    alpha1 = cone1->getConeAngle();
+  shared_ptr<Cone> cone2 = dynamic_pointer_cast<Cone,ElementarySurface>(elem2);
+  if (cone2.get())
+    alpha2 = cone2->getConeAngle();
+  if (cone1.get() && cone2.get())
+    return 0.0;   // Two cones are not handled
+  double alpha = fabs(alpha1) + fabs(alpha2);
+  shared_ptr<Plane> plane;
+  if (elem1->instanceType() == Class_Plane)
+    plane = dynamic_pointer_cast<Plane,ElementarySurface>(elem1);
+  else if (elem2->instanceType() == Class_Plane)
+    plane = dynamic_pointer_cast<Plane,ElementarySurface>(elem2);
+  int state = (plane.get()) ? 1 : 2;
+  shared_ptr<ElementarySurface> rotational;
+  if (elem1->instanceType() == Class_Plane)
+    rotational = elem2;
+  else if (elem2->instanceType() == Class_Plane)
+    rotational = elem1;
+  else if (cone1.get())
+    rotational = elem2;  // elem2 is expected to be a cylinder
+  else if (cone2.get())
+    rotational = elem1;
+  Point axis = rotational->direction();
+  Point normal = (plane.get()) ? plane->direction() : axis;
+  if (state == 1)
+    normal *= sgn;
+  if ((state == 1 && elem2->instanceType() == Class_Plane) ||
+      (state == 2 && elem2->instanceType() == Class_Cylinder))
+    std::swap(out1,out2);  // Call by value means this swap stays local
+  
+  // Compute minor radius of torus
+  double lrad = 0.0;
+  int lnmb = 0;
+  double beta = (plane.get()) ? 0.5*M_PI + alpha : M_PI-fabs(alpha);
+  //double phi = beta - 2.0*alpha;
+  double fac = 1.0/sin(0.5*beta);
+  double div = fac - 1.0;
+  Point loc = rotational->location();
+  Point locp = (plane.get()) ? plane->location() :
+    cv->ParamCurve::point(0.5*(cv->startparam()+cv->endparam()));;
+  double rd = (locp-loc)*axis;
+  Point centr = loc + rd*axis;
+  for (size_t kj=0; kj<blend_pts.size(); ++kj)
+    {
+      for (size_t ki=0; ki<blend_pts[kj].size(); ++ki)
+	{
+	  Vector3D xyz = blend_pts[kj][ki]->getPoint();
+	  Point ptpos(xyz[0], xyz[1], xyz[2]);
+
+	  // Localize with respect to the guiding curve(s)
+	  double tpar, dist;
+	  Point close;
+	  cv->closestPoint(ptpos, cv->startparam(), cv->endparam(),
+			   tpar, close, dist);
+
+	  // Define line in the point between the adjacent surfaces
+	  vector<Point> der(2);
+	  cv->point(der, tpar, 1);
+	  der[1].normalize();
+	  Point dir1 = der[1].cross(normal);
+	  Point vec1 = der[0] - centr;
+	  if ((dir1*vec1 < 0.0 && (!out2)) || (dir1*vec1 > 0.0 && out2))
+	    dir1 *= -1;
+	  Point dir2 = (out1) ? normal : -normal;
+	  Point dir3;
+	  if (alpha > 0)
+	    {
+	      Matrix3D mat;
+	      if (state == 1)
+		mat.setToRotation(-alpha, der[1][0], der[1][1], der[1][2]);
+	      else
+		mat.setToRotation(0.5*beta, der[1][0], der[1][1], der[1][2]);
+	      Vector3D dir2_2(dir2[0], dir2[1], dir2[2]);
+	      Vector3D dir3_2 = mat*dir2_2;
+	      dir3 = Point(dir3_2[0], dir3_2[1], dir3_2[2]);
+	    }
+	  else
+	    dir3 = dir2;
+	  Point dir4 = (state == 1 )? 0.5*(dir1 + dir3) : dir3;
+	  if (state == 2 && (!out2))
+	    dir4 *= -1;
+#ifdef DEBUG_BLEND
+	  std::ofstream of("midlin.g2");
+	  of << "410 1 0 4 0 255 0 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir1 << std::endl;
+	  of << "410 1 0 4 255 0 0 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir4 << std::endl;
+	  of << "410 1 0 4 0 0 255 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir2 << std::endl;
+	  of << "410 1 0 4 100 100 55 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir3 << std::endl;
+	      
+#endif
+	  shared_ptr<Line> line(new Line(der[0], dir4));
+	  line->setParameterInterval(-2*width, 2*width);
+	  double lpar, ldist;
+	  Point lclose;
+	  line->closestPoint(ptpos, line->startparam(), line->endparam(),
+			     lpar, lclose, ldist);
+	  if (ldist <= approx_tol_)
+	    {
+	      double dd2 = close.dist(lclose);
+	      double xlen = dd2/div;
+	      lrad += xlen;
+	      d2 += dd2;
+	      lnmb++;
+	    }
+	  int stop_break = 1;
+	}
+    }
+
+
+  if (lnmb > 0)
+    {
+      lrad /= (double)lnmb;
+     d2 /= (double)lnmb;
+    }
+
+  return lrad;
+}
+
+//===========================================================================
+bool
+RevEng::getTorusParameters(shared_ptr<ElementarySurface> elem1,
+			   shared_ptr<ElementarySurface> elem2, Point pos,
+			   double radius, double d2, bool out1, bool out2, int sgn,
+			   double& Rrad, Point& centre, Point& normal, Point& Cx,
+			   bool check_common)
+//===========================================================================
+{
+  double alpha1 = 0.0, alpha2 = 0.0;
+  shared_ptr<Cone> cone1 = dynamic_pointer_cast<Cone,ElementarySurface>(elem1);
+  if (cone1.get())
+    alpha1 = cone1->getConeAngle();
+  shared_ptr<Cone> cone2 = dynamic_pointer_cast<Cone,ElementarySurface>(elem2);
+  if (cone2.get())
+    alpha2 = cone2->getConeAngle();
+  if (cone1.get() && cone2.get())
+    return false;   // Two cones are not handled
+  double alpha = fabs(alpha1) + fabs(alpha2);
+  shared_ptr<Plane> plane;
+  if (elem1->instanceType() == Class_Plane)
+    plane = dynamic_pointer_cast<Plane,ElementarySurface>(elem1);
+  else if (elem2->instanceType() == Class_Plane)
+    plane = dynamic_pointer_cast<Plane,ElementarySurface>(elem2);
+  int state = (plane.get()) ? 1 : 2;
+  shared_ptr<ElementarySurface> rotational;
+  if (elem1->instanceType() == Class_Plane)
+    rotational = elem2;
+  else if (elem2->instanceType() == Class_Plane)
+    rotational = elem1;
+  else if (cone1.get())
+    rotational = elem2;  // elem2 is expected to be a cylinder
+  else if (cone2.get())
+    rotational = elem1;
+  Point axis = rotational->direction();
+  normal = (plane.get()) ? plane->direction() : axis;
+  if (state == 1)
+    normal *= sgn;
+  Point loc = rotational->location();
+  if ((state == 1 && elem2->instanceType() == Class_Plane) ||
+      (state == 2 && elem2->instanceType() == Class_Cylinder))
+    std::swap(out1,out2);  // Call by value means this swap stays local
+  
+  double rd = (pos-loc)*axis;
+  Point centr = loc + rd*axis;
+  double beta = (plane.get()) ? 0.5*M_PI + alpha : M_PI-fabs(alpha);
+  double phi = beta - 2.0*alpha;
+  double phi2 = 0.5*(M_PI - beta);
+  int sgn1 = (out1) ? -1 : 1;
+  int sgn2 = out2 ? -1 : 1;
+  if (state == 2)
+    {
+      sgn2 *= -1;
+    }
+  double hh = (state == 1) ? radius : sgn2*(radius + d2)*sin(phi2);
+  centre = centr - sgn1*hh*normal;
+  Rrad = rotational->radius(0.0, rd);
+  double sd = (state == 1) ? radius/sin(phi) : (radius + d2)*cos(phi2);
+  Cx = rotational->direction2();
+  Rrad += (sgn2*sd);
+  if (radius > Rrad && check_common)
+    return false;
+  
+#ifdef DEBUG_BLEND
+  shared_ptr<Torus> torus(new Torus(Rrad, radius, centre, normal, Cx));
+  // if (sgn2 < 0)
+  //   {
+  //     RectDomain dom = torus->getParameterBounds();
+  //     torus->setParameterBounds(dom.umin(), dom.vmin()-M_PI,
+  // 				dom.umax(), dom.vmax()-M_PI);
+  //   }
+  std::ofstream of2("tor_blend.g2");
+  torus->writeStandardHeader(of2);
+  torus->write(of2);
+  RectDomain dom2 = torus->getParameterBounds();
+  vector<shared_ptr<ParamCurve> > tmp_cvs = torus->constParamCurves(dom2.vmin(), true);
+  tmp_cvs[0]->writeStandardHeader(of2);
+  tmp_cvs[0]->write(of2);
+  double tf = (sgn2 == 1) ? 0.5 : 1.5;
+  vector<shared_ptr<ParamCurve> > tmp_cvs2 =
+    torus->constParamCurves(dom2.vmin()+tf*M_PI, true);
+  tmp_cvs2[0]->writeStandardHeader(of2);
+  tmp_cvs2[0]->write(of2);
+#endif
+  return true;
+}
+
+//===========================================================================
+shared_ptr<Torus>
+RevEng::torusBlend(vector<vector<RevEngPoint*> >& blend_pts,
+		   shared_ptr<CurveOnSurface>& cv,
+		   shared_ptr<ElementarySurface> elem1,
+		   shared_ptr<ElementarySurface> elem2,
+		   double width, bool out1, bool out2, int sgn)
+//===========================================================================
+{
+  shared_ptr<Torus> torus;
+
+  double alpha1 = 0.0, alpha2 = 0.0;
+  shared_ptr<Cone> cone1 = dynamic_pointer_cast<Cone,ElementarySurface>(elem1);
+  if (cone1.get())
+    alpha1 = cone1->getConeAngle();
+  shared_ptr<Cone> cone2 = dynamic_pointer_cast<Cone,ElementarySurface>(elem2);
+  if (cone2.get())
+    alpha2 = cone2->getConeAngle();
+  if (cone1.get() && cone2.get())
+    return torus;   // Two cones are not handled
+  double alpha = fabs(alpha1) + fabs(alpha2);
+  shared_ptr<Plane> plane;
+  if (elem1->instanceType() == Class_Plane)
+    plane = dynamic_pointer_cast<Plane,ElementarySurface>(elem1);
+  else if (elem2->instanceType() == Class_Plane)
+    plane = dynamic_pointer_cast<Plane,ElementarySurface>(elem2);
+  int state = (plane.get()) ? 1 : 2;
+  shared_ptr<ElementarySurface> rotational;
+  if (elem1->instanceType() == Class_Plane)
+    rotational = elem2;
+  else if (elem2->instanceType() == Class_Plane)
+    rotational = elem1;
+  else if (cone1.get())
+    rotational = elem2;  // elem2 is expected to be a cylinder
+  else if (cone2.get())
+    rotational = elem1;
+  Point axis = rotational->direction();
+  Point normal = (plane.get()) ? plane->direction() : axis;
+  if (state == 1)
+    normal *= sgn;
+  if ((state == 1 && elem2->instanceType() == Class_Plane) ||
+      (state == 2 && elem2->instanceType() == Class_Cylinder))
+    std::swap(out1,out2);  // Call by value means this swap stays local
+  
+  // Compute minor radius of torus
+  double lrad = 0.0;
+  double d2 = 0.0;
+  int lnmb = 0;
+  double beta = (plane.get()) ? 0.5*M_PI + alpha : M_PI-fabs(alpha);
+  double phi = beta - 2.0*alpha;
+  double fac = 1.0/sin(0.5*beta);
+  double div = fac - 1.0;
+  Point loc = rotational->location();
+  Point locp = (plane.get()) ? plane->location() :
+    cv->ParamCurve::point(0.5*(cv->startparam()+cv->endparam()));;
+  double rd = (locp-loc)*axis;
+  Point centr = loc + rd*axis;
+  for (size_t kj=0; kj<blend_pts.size(); ++kj)
+    {
+      for (size_t ki=0; ki<blend_pts[kj].size(); ++ki)
+	{
+	  Vector3D xyz = blend_pts[kj][ki]->getPoint();
+	  Point ptpos(xyz[0], xyz[1], xyz[2]);
+
+	  // Localize with respect to the guiding curve(s)
+	  double tpar, dist;
+	  Point close;
+	  cv->closestPoint(ptpos, cv->startparam(), cv->endparam(),
+			   tpar, close, dist);
+
+	  // Define line in the point between the adjacent surfaces
+	  vector<Point> der(2);
+	  cv->point(der, tpar, 1);
+	  der[1].normalize();
+	  Point dir1 = der[1].cross(normal);
+	  Point vec1 = der[0] - centr;
+	  if ((dir1*vec1 < 0.0 && (!out2)) || (dir1*vec1 > 0.0 && out2))
+	    dir1 *= -1;
+	  Point dir2 = (out1) ? normal : -normal;
+	  Point dir3;
+	  if (alpha > 0)
+	    {
+	      Matrix3D mat;
+	      if (state == 1)
+		mat.setToRotation(-alpha, der[1][0], der[1][1], der[1][2]);
+	      else
+		mat.setToRotation(0.5*beta, der[1][0], der[1][1], der[1][2]);
+	      Vector3D dir2_2(dir2[0], dir2[1], dir2[2]);
+	      Vector3D dir3_2 = mat*dir2_2;
+	      dir3 = Point(dir3_2[0], dir3_2[1], dir3_2[2]);
+	    }
+	  else
+	    dir3 = dir2;
+	  Point dir4 = (state == 1 )? 0.5*(dir1 + dir3) : dir3;
+	  if (state == 2 && (!out2))
+	    dir4 *= -1;
+#ifdef DEBUG_BLEND
+	  std::ofstream of("midlin.g2");
+	  of << "410 1 0 4 0 255 0 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir1 << std::endl;
+	  of << "410 1 0 4 255 0 0 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir4 << std::endl;
+	  of << "410 1 0 4 0 0 255 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir2 << std::endl;
+	  of << "410 1 0 4 100 100 55 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << der[0] << " " << der[0]+dir3 << std::endl;
+	      
+#endif
+	  shared_ptr<Line> line(new Line(der[0], dir4));
+	  line->setParameterInterval(-2*width, 2*width);
+	  double lpar, ldist;
+	  Point lclose;
+	  line->closestPoint(ptpos, line->startparam(), line->endparam(),
+			     lpar, lclose, ldist);
+	  if (ldist <= approx_tol_)
+	    {
+	      double dd2 = close.dist(lclose);
+	      double xlen = dd2/div;
+	      lrad += xlen;
+	      d2 += dd2;
+	      lnmb++;
+	    }
+	  int stop_break = 1;
+	}
+    }
+
+
+  if (lnmb > 0)
+    {
+      lrad /= (double)lnmb;
+      d2 /= (double)lnmb;
+    }
+
+  int sgn1 = (out1) ? -1 : 1;
+  int sgn2 = out2 ? -1 : 1;
+  if (state == 2)
+    {
+      sgn2 *= -1;
+    }
+  double phi2 = 0.5*(M_PI - beta);
+  double hh = (state == 1) ? lrad : sgn2*(lrad + d2)*sin(phi2);
+  Point pos = centr - sgn1*hh*normal;
+  double Rrad = rotational->radius(0.0, rd);
+  double sd = (state == 1) ? lrad/sin(phi) : (lrad + d2)*cos(phi2);
+  Point Cx = rotational->direction2();
+  torus = shared_ptr<Torus>(new Torus(Rrad+sgn2*sd, lrad, pos, normal, Cx));
+  if (sgn2 > 0)
+    {
+      RectDomain dom = torus->getParameterBounds();
+      try {
+	torus->setParameterBounds(dom.umin(), dom.vmin()-M_PI,
+				  dom.umax(), dom.vmax()-M_PI);
+      }
+      catch (...)
+	{
+	}
+    }
+  
+#ifdef DEBUG_BLEND
+  std::ofstream of2("tor_blend.g2");
+  torus->writeStandardHeader(of2);
+  torus->write(of2);
+  RectDomain dom2 = torus->getParameterBounds();
+  vector<shared_ptr<ParamCurve> > tmp_cvs = torus->constParamCurves(dom2.vmin(), true);
+  tmp_cvs[0]->writeStandardHeader(of2);
+  tmp_cvs[0]->write(of2);
+  double tf = (sgn2 == 1) ? 0.5 : 1.5;
+  vector<shared_ptr<ParamCurve> > tmp_cvs2 =
+    torus->constParamCurves(dom2.vmin()+tf*M_PI, true);
+  tmp_cvs2[0]->writeStandardHeader(of2);
+  tmp_cvs2[0]->write(of2);
+#endif
+  return torus;
+}
+
+//===========================================================================
+double
+RevEng::computeCylinderRadius(vector<vector<RevEngPoint*> > blend_pts,
+			    double width, const Point& pos, const Point& axis,
+			    const Point& dir1, const Point& dir2)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  double upar2, vpar2, dist2;
+  Point close, surfnorm, close2;
+  double alpha = dir1.angle(dir2);
+  Point linedir = width*dir1 + width*dir2;
+  Point planenorm = linedir.cross(axis);
+  planenorm.normalize();
+  shared_ptr<Plane> plane(new Plane(pos, planenorm, linedir));
+  double lrad = 0.0;
+  int lnmb = 0;
+  double fac = 1.0/sin(0.5*alpha);
+  double div = fac - 1.0;
+  for (size_t kj=0; kj<blend_pts.size(); ++kj)
+    {
+      for (size_t ki=0; ki<blend_pts[kj].size(); ++ki)
+	{
+	  Vector3D xyz = blend_pts[kj][ki]->getPoint();
+	  Point ptpos(xyz[0], xyz[1], xyz[2]);
+
+	  plane->closestPoint(ptpos, upar2, vpar2, close2, dist2, eps);
+	  if (dist2 <= approx_tol_)
+	    {
+	      Point pos2 = plane->ParamSurface::point(0.0, vpar2);
+	      double dd2 = pos2.dist(close2);
+	      double xlen = dd2/div;
+	      lrad += xlen;
+	      lnmb++;
+	    }
+	}
+    }
+  if (lnmb > 0)
+    lrad /= (double)lnmb;
+  return lrad;
+}
+
+
+//===========================================================================
+shared_ptr<Cylinder>
+RevEng::createCylinderBlend(vector<vector<RevEngPoint*> > blend_pts,
+			    double rad1, const Point& pos, const Point& axis,
+			    const Point& dir1, const Point& dir2, int sgn)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  double upar2, vpar2, dist2;
+  Point close, surfnorm, close2;
+  double alpha = dir1.angle(dir2);
+  Point linedir = rad1*dir1 + rad1*dir2;
+  Point planenorm = linedir.cross(axis);
+  planenorm.normalize();
+  shared_ptr<Plane> plane(new Plane(pos, planenorm, linedir));
+  double lrad = 0.0;
+  int lnmb = 0;
+  double fac = 1/sin(0.5*alpha);
+  double div = fac - 1.0;
+  for (size_t kj=0; kj<blend_pts.size(); ++kj)
+    {
+      for (size_t ki=0; ki<blend_pts[kj].size(); ++ki)
+	{
+	  Vector3D xyz = blend_pts[kj][ki]->getPoint();
+	  Point ptpos(xyz[0], xyz[1], xyz[2]);
+
+	  plane->closestPoint(ptpos, upar2, vpar2, close2, dist2, eps);
+	  if (dist2 <= approx_tol_)
+	    {
+	      Point pos2 = plane->ParamSurface::point(0.0, vpar2);
+	      double dd2 = pos2.dist(close2);
+	      double xlen = dd2/div;
+	      lrad += xlen;
+	      lnmb++;
+	    }
+	}
+    }
+  if (lnmb > 0)
+    lrad /= (double)lnmb;
+  
+  Point Cx = sgn*(dir1 + dir2);
+  Point Cy = axis.cross(Cx);
+  // Point pos2;
+  // double rad2;
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+
+  Point vec = dir1 + dir2;
+  vec.normalize();
+  Point pos3 = pos + sgn*fac*lrad*vec;
+  shared_ptr<Cylinder> cyl(new Cylinder(lrad, pos3, axis, Cx));
+  double diag = low.dist(high);
+  cyl->setParamBoundsV(-0.5*diag,0.5*diag);
+  
+#ifdef DEBUG_BLEND
+  std::ofstream of("blend_cyl2.g2");
+  cyl->writeStandardHeader(of);
+  cyl->write(of);
+#endif
+
+  
+  return cyl;
+}
+
+
+
+//===========================================================================
+void RevEng::surfaceExtractOutput(int idx,
+				  vector<vector<RevEngPoint*> > out_groups,
+				  vector<HedgeSurface*> prev_surfs)
+//===========================================================================
+{
+  for (size_t kr=0; kr<out_groups.size(); ++kr)
+    {
+      for (size_t kh=0; kh<out_groups[kr].size(); ++kh)
+	out_groups[kr][kh]->unsetRegion();  
+    }
+  
+  int classtype = (idx >= 0) ? regions_[idx]->getClassification() :
+    CLASSIFICATION_UNDEF;
+  for (size_t kr=0; kr<out_groups.size(); ++kr)
+    {
+      shared_ptr<RevEngRegion> reg(new RevEngRegion(classtype,
+						    edge_class_type_,
+						    out_groups[kr]));
+      if (idx >= 0)
+	reg->setPreviousReg(regions_[idx].get());
+      reg->setRegionAdjacency();
+#ifdef DEBUG_CHECK
+      bool connect = reg->isConnected();
+      connect = reg->isConnected();
+      if (!connect)
+	{
+	  std::cout << "surfaceExtractOutput, disconnected region " << idx << std::endl;
+	}
+#endif
+      bool integrate = reg->integrateInAdjacent(mean_edge_len_,
+						min_next_, max_next_,
+						approx_tol_, 0.5,
+						max_nmb_outlier_,
+						(idx >= 0) ? regions_[idx].get() : 0);
+      if (!integrate)
+	regions_.push_back(reg);
+    }	  
+  for (size_t kr=0; kr<prev_surfs.size(); ++kr)
+    {
+      size_t kj;
+      for (kj=0; kj<surfaces_.size(); ++kj)
+	if (surfaces_[kj].get() == prev_surfs[kr])
+	  break;
+      if (kj < surfaces_.size())
+	surfaces_.erase(surfaces_.begin()+kj);
+    }
+}
+
+//===========================================================================
+bool RevEng::segmentByPlaneGrow(int ix, int min_point_in, double angtol)
+//===========================================================================
+{
+#ifdef DEBUG
+  std::ofstream ofreg("segment_reg.g2");
+  regions_[ix]->writeRegionInfo(ofreg);
+#endif
+
+  vector<shared_ptr<HedgeSurface> > plane_sfs;
+  vector<HedgeSurface*> prev_surfs;
+  vector<vector<RevEngPoint*> > out_groups;
+  regions_[ix]->segmentByPlaneGrow(mainaxis_, approx_tol_, angtol, min_point_in, 
+				   plane_sfs, prev_surfs, out_groups);
+#ifdef DEBUG
+  for (size_t ki=0; ki<plane_sfs.size(); ++ki)
+    {
+      plane_sfs[ki]->surface()->writeStandardHeader(ofreg);
+      plane_sfs[ki]->surface()->write(ofreg);
+    }
+#endif
+  
+  if (out_groups.size() > 0 || prev_surfs.size() > 0)
+    surfaceExtractOutput(ix, out_groups, prev_surfs);
+  if (plane_sfs.size() > 0)
+    surfaces_.insert(surfaces_.end(), plane_sfs.begin(), plane_sfs.end());
+
+  bool segmented = (out_groups.size() > 0);
+  return segmented;
+}
+
+//===========================================================================
+bool RevEng::segmentByAxis(int ix, int min_point_in)
+//===========================================================================
+{
+  double angtol = 5.0*anglim_;
+  vector<shared_ptr<HedgeSurface> > hedgesfs;
+  vector<shared_ptr<RevEngRegion> > added_reg;
+  vector<vector<RevEngPoint*> > separate_groups;
+  vector<RevEngPoint*> single_points;
+  bool segmented = regions_[ix]->extractCylByAxis(mainaxis_, min_point_in,
+						  min_point_region_,
+					       approx_tol_, angtol,
+					       prefer_elementary_,
+					       hedgesfs, added_reg,
+					       separate_groups,
+					       single_points);
+  if (segmented && single_points.size() > 0)
+    single_points_.insert(single_points_.end(), single_points.begin(),
+			  single_points.end());
+#ifdef DEBUG
+  if (segmented)
+    {
+      std::ofstream of("seg_by_axis.g2");
+      int num = regions_[ix]->numPoints();
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of <<  num << std::endl;
+      for (int ka=0; ka<num; ++ka)
+	of << regions_[ix]->getPoint(ka)->getPoint() << std::endl;
+
+      for (size_t ki=0; ki<separate_groups.size(); ++ki)
+	{
+	  of << "400 1 0 4 0 255 0 255" << std::endl;
+	  of <<  separate_groups[ki].size() << std::endl;
+	  for (int ka=0; ka<(int)separate_groups[ki].size(); ++ka)
+	    of << separate_groups[ki][ka]->getPoint() << std::endl;
+	}
+    }
+#endif
+  
+  if (separate_groups.size() > 0)
+    {
+      vector<HedgeSurface*> prev_surfs;
+      surfaceExtractOutput(ix, separate_groups, prev_surfs);
+    }
+  
+  if (added_reg.size() > 0)
+    regions_.insert(regions_.end(), added_reg.begin(), added_reg.end());
+  if (hedgesfs.size() > 0)
+    surfaces_.insert(surfaces_.end(), hedgesfs.begin(), hedgesfs.end());
+  
+  return segmented;
+}
+
+//===========================================================================
+bool RevEng::segmentByContext(int ix, int min_point_in, double angtol, bool first)
+//===========================================================================
+{
+#ifdef DEBUG_DIV
+  vector<RevEngPoint*> branchpt = regions_[ix]->extractBranchPoints();
+  if (branchpt.size() > 0)
+    {
+      std::ofstream ofb("branch_pts_seg.g2");
+      ofb << "400 1 0 4 0 0 0 255" << std::endl;
+      ofb << branchpt.size() << std::endl;
+      for (size_t ki=0; ki<branchpt.size(); ++ki)
+	ofb << branchpt[ki]->getPoint() << std::endl;
+    }
+  
+#endif
+  
+  vector<vector<RevEngPoint*> > separate_groups;
+  vector<RevEngRegion*> adj_planar = regions_[ix]->fetchAdjacentPlanar();
+  vector<shared_ptr<HedgeSurface> > hedgesfs;
+  vector<shared_ptr<RevEngRegion> > added_reg;
+  vector<HedgeSurface*> prevsfs;
+  bool segmented = false;
+  if (adj_planar.size() > 1)
+    {
+      segmented = regions_[ix]->segmentByPlaneAxis(mainaxis_, min_point_in,
+						   min_point_region_,
+						   approx_tol_, angtol,
+						   prefer_elementary_,
+						   adj_planar, hedgesfs,
+						   added_reg,
+						   prevsfs, separate_groups);
+    }
+
+  if (!segmented)
+    {
+      // Extend with cylindrical
+      vector<RevEngRegion*> adj_cyl = regions_[ix]->fetchAdjacentCylindrical();
+      if (adj_cyl.size() > 0)
+	adj_planar.insert(adj_planar.end(), adj_cyl.begin(), adj_cyl.end());
+      if (adj_planar.size() > 0)
+	segmented =
+	  regions_[ix]->segmentByAdjSfContext(mainaxis_, min_point_in,
+					      min_point_region_,
+					       approx_tol_, angtol,
+					       adj_planar, separate_groups);
+    }
+  
+  if (!segmented)
+    {
+      // Search for context direction
+      double angtol2 = 2.0*angtol;
+
+      Point direction = regions_[ix]->directionFromAdjacent(angtol);
+      vector<vector<RevEngPoint*> > separate_groups2;
+      if (direction.dimension() == 3)
+	segmented = regions_[ix]->segmentByDirectionContext(min_point_in, approx_tol_,
+							    direction, angtol2,
+							    separate_groups2);
+      if (separate_groups2.size() > 0)
+	separate_groups.insert(separate_groups.end(), separate_groups2.begin(),
+			       separate_groups2.end());
+      if (segmented && (!regions_[ix]->hasSurface()))
+	{
+	  double angtol = -1.0;
+	  int pass = 1;
+	  bool found = recognizeOneSurface(ix, min_point_in, angtol, pass);
+	  int stop_break = 1;
+	}
+    }
+  
+#ifdef DEBUG
+  if (segmented)
+    {
+      std::ofstream of("seg_by_context.g2");
+      int num = regions_[ix]->numPoints();
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of <<  num << std::endl;
+      for (int ka=0; ka<num; ++ka)
+	of << regions_[ix]->getPoint(ka)->getPoint() << std::endl;
+
+      for (size_t ki=0; ki<separate_groups.size(); ++ki)
+	{
+	  of << "400 1 0 4 0 255 0 255" << std::endl;
+	  of <<  separate_groups[ki].size() << std::endl;
+	  for (int ka=0; ka<(int)separate_groups[ki].size(); ++ka)
+	    of << separate_groups[ki][ka]->getPoint() << std::endl;
+	}
+    }
+#endif
+  
+  if (separate_groups.size() > 0)
+    {
+      vector<HedgeSurface*> prev_surfs;
+      surfaceExtractOutput(ix, separate_groups, prev_surfs);
+    }
+
+  if (added_reg.size() > 0)
+    regions_.insert(regions_.end(), added_reg.begin(), added_reg.end());
+  if (hedgesfs.size() > 0)
+    surfaces_.insert(surfaces_.end(), hedgesfs.begin(), hedgesfs.end());
+  
+  return segmented;
+}
+
+//===========================================================================
+void RevEng::growSurface(int& ix, int pass)
+//===========================================================================
+{
+  vector<RevEngRegion*> grown_regions;
+  int min_nmb = 5*min_point_region_;  // Should be set from distribution of how many
+	  // points the regions have
+  double angtol = 5.0*anglim_;
+  vector<HedgeSurface*> adj_surfs;
+  vector<RevEngEdge*> adj_edgs;
+  regions_[ix]->growWithSurf(mainaxis_, min_point_region_,
+			     approx_tol_, angtol, grown_regions,
+			     adj_surfs, adj_edgs, (pass>1));
+  updateRegionsAndSurfaces(ix, grown_regions, adj_surfs);
+  for (size_t ki=0; ki<adj_edgs.size(); ++ki)
+    {
+      size_t kj;
+      for (kj=0; kj<edges_.size(); ++kj)
+	if (edges_[kj].get() == adj_edgs[ki])
+	  break;
+      if (kj < edges_.size())
+	edges_.erase(edges_.begin()+kj);
+    }
+}
+
+//===========================================================================
+void RevEng::growBlendSurface(int& ix)
+//===========================================================================
+{
+  // Collect associated blend surfaces
+  RevEngEdge *edge = regions_[ix]->getBlendEdge();
+  if (!edge)
+    return;
+
+#ifdef DEBUG_BLEND
+  std::ofstream of("blend_grow.g2");
+  regions_[ix]->writeRegionPoints(of);
+#endif
+
+  vector<RevEngRegion*> next_blend;
+  RevEngRegion *adj[2];
+  edge->getAdjacent(adj[0], adj[1]);
+  for (int ka=0; ka<2; ++ka)
+    {
+      if (!adj[ka])
+	continue;
+      vector<RevEngEdge*> rev_edgs = adj[ka]->getAllRevEdges();
+      for (size_t ki=0; ki<rev_edgs.size(); ++ki)
+	{
+	  RevEngRegion *blendreg = rev_edgs[ki]->getBlendRegSurf();
+	  if (blendreg)
+	    {
+	      if (blendreg == regions_[ix].get())
+		continue;
+	      size_t kj=0;
+	      for (kj=0; kj<next_blend.size(); ++kj)
+		if (next_blend[kj] == blendreg)
+		  break;
+	      if (kj < next_blend.size())
+		continue;
+	      next_blend.push_back(blendreg);
+	    }
+	}
+    }
+
+#ifdef DEBUG_BLEND
+  for (size_t kr=0; kr<next_blend.size(); ++kr)
+    next_blend[kr]->writeRegionPoints(of);
+#endif
+  
+  vector<RevEngRegion*> grown_regions;
+  double angtol = 5.0*anglim_;
+  vector<HedgeSurface*> adj_surfs;
+  vector<vector<RevEngPoint*> > added_regs;
+  regions_[ix]->growBlendSurf(next_blend, approx_tol_, angtol,
+			      grown_regions, added_regs);
+
+  updateRegionsAndSurfaces(ix, grown_regions, adj_surfs);
+  if (added_regs.size() > 0)
+    {
+      vector<HedgeSurface*> prev_surfs;
+      surfaceExtractOutput(ix, added_regs, prev_surfs);
+    }
+}
+
+//===========================================================================
+void RevEng::growMasterSurface(int& ix)
+//===========================================================================
+{
+
+#ifdef DEBUG_BLEND
+  std::ofstream of("master_grow.g2");
+  regions_[ix]->writeRegionPoints(of);
+#endif
+
+  vector<RevEngRegion*> grown_regions;
+  double angtol = 5.0*anglim_;
+  vector<HedgeSurface*> adj_surfs;
+  int small_lim = min_point_region_/20;
+  regions_[ix]->joinToCurrent(approx_tol_, angtol, small_lim,
+			      grown_regions);
+
+  updateRegionsAndSurfaces(ix, grown_regions, adj_surfs);
+}
+
+
+//===========================================================================
+void RevEng::updateRegionsAndSurfaces(int& ix, vector<RevEngRegion*>& grown_regions,
+				      vector<HedgeSurface*>& adj_surfs)
+//===========================================================================
+{
+  if (grown_regions.size() > 0)
+    {
+      for (size_t kr=0; kr<grown_regions.size(); ++kr)
+	{
+// #ifdef DEBUG_CHECK
+// 	  bool connect = grown_regions[kr]->isConnected();
+// 	  connect = grown_regions[kr]->isConnected();
+// 	  if (!connect)
+// 	    std::cout << "updateRegionsAndSurfaces, disconnected region " << ix << kr << std::endl;
+// #endif
+	  size_t kj;
+	  for (kj=0; kj<regions_.size(); )
+	    {
+	      if ((int)kj == ix)
+		{
+		  ++kj;
+		  continue;
+		}
+
+	      if (grown_regions[kr] == regions_[kj].get())
+		{
+		  regions_.erase(regions_.begin()+kj);
+		  if ((int)kj < ix)
+		    --ix;
+		}
+	      else
+		++kj;
+	    }
+	}
+      for (size_t kr=0; kr<adj_surfs.size(); ++kr)
+	{
+	  size_t kj;
+	  for (kj=0; kj<surfaces_.size(); ++kj)
+	    if (surfaces_[kj].get() == adj_surfs[kr])
+	      break;
+	  if (kj < surfaces_.size())
+	    surfaces_.erase(surfaces_.begin()+kj);
+	}
+    }
+
+  for (size_t kj=0; kj<regions_.size(); ++kj)
+    regions_[kj]->setVisited(false);
+
+// #ifdef DEBUG_DIV
+//   std::ofstream ofpts("sfpoints2.g2");
+//   std::ofstream ofpar("sfparpoints2.txt");
+//   int numpt = regions_[ix]->numPoints();
+//   ofpts << "400 1 0 0" << std::endl;
+//   ofpts << numpt << std::endl;
+//   for (int ka=0; ka<numpt; ++ka)
+//     {
+//       RevEngPoint *pt = regions_[ix]->getPoint(ka);
+//       ofpar << pt->getPar() << " " << pt->getPoint() << std::endl;
+//       ofpts << pt->getPoint() << std::endl;
+//     }
+// #endif
+  int stop_break = 1;
+  
+}
+
+
+//===========================================================================
+void RevEng::smallRegionSurfaces()
+//===========================================================================
+{
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  // Add possible missing edges
+  recognizeEdges();
+  
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+   
+#ifdef DEBUG
+   std::cout << "Extend blend region collection" << std::endl;
+#endif
+   for (size_t ki=0; ki<edges_.size(); ++ki)
+     {
+       extendBlendAssociation(ki);
+     }
+
+
+  int nmb_sfs = (int)surfaces_.size();
+  defineSmallRegionSurfaces();
+
+#ifdef DEBUG
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      RevEngRegion *first = regions_[ki]->getPoint(0)->region();
+      int num = regions_[ki]->numPoints();
+      for (int ka=1; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != first)
+	  std::cout << "Inconsistent region pointers, post defineSmallRegionSurfaces: " << ki << " " << ka << std::endl;
+    }
+#endif
+
+#ifdef DEBUG
+   checkConsistence("Regions10_1");
+
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions10_1" << std::endl;
+      std::ofstream of("regions10_1.g2");
+      std::ofstream ofm("mid_regions10_1.g2");
+      std::ofstream ofs("small_regions10_1.g2");
+      writeRegionStage(of, ofm, ofs);
+
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf10_1.g2");
+	  writeRegionWithSurf(of);
+	}
+    }
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges10_1.g2");
+       writeEdgeStage(ofe);
+     }
+
+   if (surfaces_.size() > 0)
+     {
+       std::ofstream ofsf("surfaces10_1.g2");
+       for (size_t kr=0; kr<surfaces_.size(); ++kr)
+	 {
+	   RevEngRegion *reg = surfaces_[kr]->getRegion(0);
+	   reg->writeSurface(ofsf);
+	   //reg->writeRegionPoints(ofsf);
+	 }
+     }
+#endif
+
+  // Update adjacency between regions
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->clearRegionAdjacency();
+    }
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  // Check if small regions surfaces really belongs to a blend
+  for (size_t ki=nmb_sfs; ki<surfaces_.size();)
+    {
+      RevEngRegion *reg = surfaces_[ki]->getRegion(0);
+      vector<HedgeSurface*> removed_sfs;
+      reg->checkEdgeAssociation(approx_tol_, min_point_region_, removed_sfs);
+      if (removed_sfs.size() > 0)
+	{
+	  for (size_t kr=0; kr<removed_sfs.size(); ++kr)
+	    {
+	      size_t kj;
+	      for (kj=0; kj<surfaces_.size(); ++kj)
+		if (surfaces_[kj].get() == removed_sfs[kr])
+		  break;
+	      if (kj < surfaces_.size())
+		surfaces_.erase(surfaces_.begin()+kj);
+	    }
+	}
+      else
+	++ki;
+    }
+  
+#ifdef DEBUG
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      int num = regions_[ki]->numPoints();
+      for (int ka=0; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+	  std::cout << "Inconsistent region pointers, post define small region surfaces: " << ki << " " << ka << std::endl;
+    }
+#endif
+   // if ((int)surfaces_.size() > nmb_sfs)
+   //   recognizeEdges();
+   
+#ifdef DEBUG
+   checkConsistence("Regions10_1_2");
+
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions10_1_2" << std::endl;
+      std::ofstream of("regions10_1_2.g2");
+      std::ofstream ofm("mid_regions10_1_2.g2");
+      std::ofstream ofs("small_regions10_1_2.g2");
+      writeRegionStage(of, ofm, ofs);
+
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf10_1.g2");
+	  writeRegionWithSurf(of);
+	}
+    }
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges10_1_2.g2");
+       writeEdgeStage(ofe);
+     }
+#endif
+  
+   double angtol = 5.0*anglim_;
+   for (int ka=nmb_sfs; ka<(int)surfaces_.size(); ++ka)
+     {
+       growSmallRegionSurface(ka);
+     }
+   //#if 0
+   // Dismiss too small surfaces. First check connectivity
+   int min_sf_pts = min_point_region_/5;
+   for (int ka=0; ka<(int)regions_.size(); ++ka)
+     {
+       if ((!regions_[ka]->hasSurface()) || regions_[ka]->hasBlendEdge())
+	 continue;
+       vector<vector<RevEngPoint*> > separate_groups;
+       vector<HedgeSurface*> out_sfs;
+       vector<RevEngEdge*> out_edgs;
+       regions_[ka]->splitRegion(separate_groups);
+       if (separate_groups.size() > 0)
+	 regions_[ka]->updateInfo(approx_tol_, angtol);
+       if (regions_[ka]->numPoints() < min_sf_pts)
+	 {
+	   out_sfs.push_back(regions_[ka]->getSurface(0));
+	   vector<RevEngEdge*> rev_edgs = regions_[ka]->getAllRevEdges();
+	   for (size_t kr=0; kr<rev_edgs.size(); ++kr)
+	     {
+	       RevEngRegion *adj1, *adj2;
+	       rev_edgs[kr]->getAdjacent(adj1, adj2);
+	       RevEngRegion *other = (adj1 == regions_[ka].get()) ? adj2 : adj1;
+	       other->removeRevEngEdge(rev_edgs[kr]);
+	     }
+	   out_edgs.insert(out_edgs.end(), rev_edgs.begin(), rev_edgs.end());
+	   regions_[ka]->clearSurface();
+	 }
+
+       if (separate_groups.size() > 0 || out_sfs.size() > 0)
+	   surfaceExtractOutput(ka, separate_groups, out_sfs);
+       
+       for (size_t ki=0; ki<out_edgs.size(); ++ki)
+	 {
+	   size_t kj;
+	   for (kj=0; kj<edges_.size(); ++kj)
+	     if (edges_[kj].get() == out_edgs[ki])
+	       break;
+	   if (kj < edges_.size())
+	     edges_.erase(edges_.begin()+kj);
+	 }
+ 
+     }
+   //#endif 
+   recognizeEdges();
+ 
+   
+
+#ifdef DEBUG
+   checkConsistence("Regions10_2");
+
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions10_2" << std::endl;
+      std::ofstream of("regions10_2.g2");
+      std::ofstream ofm("mid_regions10_2.g2");
+      std::ofstream ofs("small_regions10_2.g2");
+      writeRegionStage(of, ofm, ofs);
+
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf10_2.g2");
+	  writeRegionWithSurf(of);
+	}
+    }
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges10_2.g2");
+       writeEdgeStage(ofe);
+     }
+   if (surfaces_.size() > 0)
+     {
+       std::ofstream ofsf("surfaces10_2.g2");
+       for (size_t kr=0; kr<surfaces_.size(); ++kr)
+	 {
+	   RevEngRegion *reg = surfaces_[kr]->getRegion(0);
+	   reg->writeSurface(ofsf);
+	   //reg->writeRegionPoints(ofsf);
+	 }
+     }
+#ifdef DEBUG
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      int num = regions_[ki]->numPoints();
+      for (int ka=0; ka<num; ++ka)
+	if (regions_[ki]->getPoint(ka)->region() != regions_[ki].get())
+	  std::cout << "Inconsistent region pointers, post grow small regions surfaces: " << ki << " " << ka << std::endl;
+    }
+#endif
+   std::cout << "Define small region surfaces, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+#endif
+   int stop_break = 1;
+}
+
+//===========================================================================
+// Service function for growSmallRegionSurface
+int selectBestAccuracy(double tol, int num, double maxd[2], double avd[2],
+		       int num_in[2], int num2_in[2])
+{
+  int ix = (avd[0] < avd[1]) ? 0 : 1;
+  int ix2 = 1 - ix;
+  double fac = 1.1;
+  if ((avd[ix2] < fac*avd[ix] && num_in[ix2] > num_in[ix] && num2_in[ix2] > num_in[ix]))
+    std::swap(ix,ix2);
+
+  return ix;
+}
+
+//===========================================================================
+void RevEng::growSmallRegionSurface(int& ix)
+//===========================================================================
+{
+  double angtol = 5.0*anglim_;
+  
+  RevEngRegion *reg = surfaces_[ix]->getRegion(0);
+  shared_ptr<ParamSurface> surf = surfaces_[ix]->surface();
+  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		  surf->instanceType() == Class_Cone);
+  int num_group_points = reg->numPoints();
+
+  // Collect feasible adjacent regions
+  vector<RevEngRegion*> adj_surf, adj_nosurf;
+  reg->getAdjCandMerge(adj_surf, adj_nosurf);
+
+#ifdef DEBUG_SMALL
+  std::ofstream of1("grow_small.g2");
+  reg->writeRegionPoints(of1);
+  reg->writeSurface(of1);
+
+  for (size_t kr=0; kr<adj_surf.size(); ++kr)
+    {
+      adj_surf[kr]->writeRegionPoints(of1);
+      adj_surf[kr]->writeSurface(of1);
+    }
+  
+  for (size_t kr=0; kr<adj_nosurf.size(); ++kr)
+    {
+      adj_nosurf[kr]->writeRegionPoints(of1);
+    }      
+#endif
+
+  // Decide on master regions with surface
+  vector<RevEngRegion*> to_remove;
+  for (size_t ki=0; ki<adj_surf.size(); ++ki)
+    {
+      // Check combined accuracy based on current region and adjacent region
+      int numpt = reg->numPoints() + adj_surf[ki]->numPoints();
+      double maxd[2], avd[2], maxd_loc[2], avd_loc[2];
+      int num_in[2], num2_in[2], num_in_loc[2], num2_in_loc[2];
+      vector<vector<double> > parvals(2);   // Only points from the other region
+      vector<vector<pair<double,double> > > distang(2);
+      int sf_flag_loc[2];
+      sf_flag_loc[0] =
+	reg->getGrowAccuracy(adj_surf[ki], approx_tol_, angtol, maxd[0], avd[0],
+			     num_in[0], num2_in[0], maxd_loc[0], avd_loc[0],
+			     num_in_loc[0], num2_in_loc[0], parvals[0], distang[0]);
+      sf_flag_loc[1] = 
+	adj_surf[ki]->getGrowAccuracy(reg, approx_tol_, angtol, maxd[1], avd[1], 
+				      num_in[1], num2_in[1], maxd_loc[1], avd_loc[1],
+				      num_in_loc[1], num2_in_loc[1], parvals[1], distang[1]);
+
+      int m_ix = selectBestAccuracy(approx_tol_, numpt, maxd, avd, num_in, num2_in);
+      if (m_ix < 0 || sf_flag_loc[m_ix] == ACCURACY_POOR || sf_flag_loc[m_ix] == NOT_SET)
+      	continue;
+
+      shared_ptr<ParamSurface> surf2 = adj_surf[ki]->getSurface(0)->surface();
+      bool cyllike2 = (surf2->instanceType() == Class_Cylinder ||
+		       surf2->instanceType() == Class_Cone);
+      int sf_flag = reg->defineSfFlag(numpt, 0, approx_tol_, num_in[m_ix], num2_in[m_ix],
+				      avd[m_ix], (m_ix==0) ? cyllike : cyllike2);
+      if (sf_flag == ACCURACY_POOR || sf_flag == NOT_SET)
+	continue;
+      
+      vector<RevEngRegion*> added_adjacent;
+      if (m_ix == 0)
+	{
+	  reg->includeAdjacentRegion(adj_surf[ki], maxd_loc[m_ix], avd_loc[m_ix],
+				     num_in_loc[m_ix], num2_in_loc[ix], parvals[m_ix],
+				     distang[m_ix], added_adjacent);
+	  to_remove.push_back(adj_surf[ki]);
+	}
+      else
+	{
+	  adj_surf[ki]->includeAdjacentRegion(reg, maxd_loc[m_ix], avd_loc[m_ix],
+					      num_in_loc[m_ix], num2_in_loc[m_ix],
+					      parvals[m_ix], distang[m_ix], added_adjacent);
+	  to_remove.push_back(reg);
+	  reg = adj_surf[ki];
+	  cyllike = cyllike2;
+	}
+
+      reg->setSurfaceFlag(sf_flag);
+      int stop_break0 = 1;
+    }
+
+  for (size_t ki=0; ki<adj_nosurf.size(); ++ki)
+    {
+      int numpt = reg->numPoints() + adj_nosurf[ki]->numPoints();
+      double maxd, maxd_loc, avd, avd_loc;
+      int num_in, num2_in, num_in_loc, num2_in_loc;
+      vector<double> parvals;
+      vector<pair<double,double> > distang;
+      int sf_flag_loc =
+	reg->getGrowAccuracy(adj_nosurf[ki], approx_tol_, angtol, maxd, avd,
+			     num_in, num2_in, maxd_loc, avd_loc,
+			     num_in_loc, num2_in_loc, parvals, distang);
+      int sf_flag = reg->defineSfFlag(numpt, 0, approx_tol_, num_in, num2_in,
+				      avd, cyllike);
+      if (sf_flag == ACCURACY_POOR || sf_flag == NOT_SET ||
+	  sf_flag_loc == ACCURACY_POOR || sf_flag_loc == NOT_SET)
+	continue;
+      
+      vector<RevEngRegion*> added_adjacent;
+      reg->includeAdjacentRegion(adj_nosurf[ki], maxd_loc, avd_loc,
+				 num_in_loc, num2_in_loc, parvals,
+				 distang, added_adjacent);
+      to_remove.push_back(adj_nosurf[ki]);
+      reg->setSurfaceFlag(sf_flag);
+    }
+
+  vector<HedgeSurface*> to_remove_sfs;
+  vector<RevEngEdge*> to_remove_edg;
+  for (size_t ki=0; ki<to_remove.size(); ++ki)
+    {
+      if (to_remove[ki]->hasSurface())
+	{
+	  int numsf = to_remove[ki]->numSurface();
+	  for (int ka=0; ka<numsf; ++ka)
+	    to_remove_sfs.push_back(to_remove[ki]->getSurface(ka));
+	}
+      vector<RevEngEdge*> rev_edgs = to_remove[ki]->getAllRevEdges();
+      for (size_t kr=0; kr<rev_edgs.size(); ++kr)
+	{
+	  RevEngRegion *adj1, *adj2;
+	  rev_edgs[kr]->getAdjacent(adj1, adj2);
+	  RevEngRegion *other = (adj1 == to_remove[ki]) ? adj2 : adj1;
+	  other->removeRevEngEdge(rev_edgs[kr]);
+	}
+      to_remove_edg.insert(to_remove_edg.end(), rev_edgs.begin(), rev_edgs.end());
+      to_remove[ki]->removeFromAdjacent();
+      to_remove[ki]->clearRegionAdjacency();
+    }
+
+  double fac = 1.5;
+  if (reg->numPoints() > (int)(fac*num_group_points) ||
+      to_remove_sfs.size() > 0)
+    {
+  vector<RevEngEdge*> rev_edges_curr = reg->getAllRevEdges();
+  for (size_t kj=0; kj<rev_edges_curr.size(); ++kj)
+    rev_edges_curr[kj]->increaseExtendCount();
+    }
+
+  for (size_t ki=0; ki<to_remove_sfs.size(); ++ki)
+    {
+      size_t kj;
+      for (kj=0; kj<surfaces_.size(); ++kj)
+	if (surfaces_[kj].get() == to_remove_sfs[ki])
+	  break;
+      if ((int)kj <= ix)
+	--ix;
+     }
+  
+  for (size_t ki=0; ki<to_remove_edg.size(); ++ki)
+    {
+      size_t kj;
+      for (kj=0; kj<edges_.size(); ++kj)
+	if (edges_[kj].get() == to_remove_edg[ki])
+	  break;
+      if (kj < edges_.size())
+	edges_.erase(edges_.begin()+kj);
+    }
+ 
+  if (to_remove.size() > 0)
+    {
+      for (size_t ki=0; ki<to_remove.size(); ++ki)
+	{
+	  to_remove[ki]->removeFromAdjacent();
+	  to_remove[ki]->clearRegionAdjacency();
+	}
+ 
+      int dummy_ix = -1;
+      updateRegionsAndSurfaces(dummy_ix, to_remove, to_remove_sfs);
+    }
+  
+#ifdef DEBUG_SMALL
+  std::ofstream of2("updated_small.g2");
+  reg->writeRegionPoints(of2);
+  reg->writeSurface(of2);
+#endif
+  int stop_break = 1;
+}
+
+//===========================================================================
+// Service functionality for defineSmallRegionSurfaces
+//
+
+void groupAdjacentRegions(vector<RevEngRegion*>& regs,
+			  vector<vector<RevEngRegion*> >& groups,
+			  vector<int>& num_group)
+{
+  for (size_t kh=0; kh<regs.size(); ++kh)
+    {
+      vector<RevEngRegion*> currgroup;
+      if (regs[kh]->visited())
+	continue;
+      regs[kh]->setVisited(true);
+      currgroup.push_back(regs[kh]);
+      for (size_t kh2=0; kh2<currgroup.size(); ++kh2)
+	{
+	  RevEngRegion *curr=currgroup[kh2];
+	  vector<RevEngRegion*> adjacent;
+	  curr->getAdjacentRegions(adjacent);
+	  for (size_t kh3=0; kh3<adjacent.size(); ++kh3)
+	    {
+	      if (adjacent[kh3]->visited())
+		continue;
+	      if (adjacent[kh3]->hasSurface())
+		continue;
+	      if (adjacent[kh3]->hasAssociatedBlend())
+		continue;
+	      size_t kh4;
+	      for (kh4=0; kh4<regs.size(); ++kh4)
+		if (adjacent[kh3] == regs[kh4])
+		  break;
+	      if (kh4 < regs.size())
+		{
+		  adjacent[kh3]->setVisited(true);
+		  currgroup.push_back(adjacent[kh3]);
+		}
+	    }
+	}
+      groups.push_back(currgroup);
+    }
+
+  for (size_t kh=0; kh<regs.size(); ++kh)
+    regs[kh]->setVisited(false);
+
+  num_group.resize(groups.size(), 0);
+  for (size_t kr=0; kr<groups.size(); ++kr)
+    for (size_t kh=0; kh<groups[kr].size(); ++kh)
+      num_group[kr] += groups[kr][kh]->numPoints();
+}
+
+void disconnectRegion(RevEngRegion* reg, vector<RevEngRegion*>& removed_regs,
+		      vector<shared_ptr<RevEngRegion> >& added_regs,
+		      vector<RevEngRegion*>& nosf_reg)
+{
+  reg->removeFromAdjacent();
+  reg->clearRegionAdjacency();
+ auto it = std::find(nosf_reg.begin(), nosf_reg.end(), reg);
+  if (it != nosf_reg.end())
+    nosf_reg.erase(it);
+  size_t ki;
+  for (ki=0; ki<added_regs.size(); ++ki)
+    if (added_regs[ki].get() == reg)
+      break;
+  if (ki == added_regs.size())
+    removed_regs.push_back(reg);
+  else
+    added_regs.erase(added_regs.begin()+ki);
+ }
+
+
+void sortAlongAxis(vector<RevEngPoint*>& points, const Point& pos,
+		   const Point& axis, vector<double>& ppar, double delta,
+		   vector<vector<RevEngPoint*> >& sorted, size_t nsort,
+		   vector<pair<RevEngPoint*, double> >& remaining,
+		   double& tmin, double& tmax)
+ {
+  tmin = std::numeric_limits<double>::max();
+  tmax = std::numeric_limits<double>::lowest();
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point point(xyz[0], xyz[1], xyz[2]);
+      double tpar = (point - pos)*axis;
+      tmin = std::min(tmin, tpar);
+      tmax = std::max(tmax, tpar);
+
+      if (ppar.size() == 0)
+	{
+	  sorted[nsort].push_back(points[ki]);
+	}
+      else
+	{
+	  size_t kr;
+	  for (kr=0; kr<ppar.size() && ppar[kr]<tpar; ++kr);
+	  if (kr > 0 && kr == ppar.size())
+	    --kr;
+	  if (fabs(tpar-ppar[kr]) < delta || (kr>0 && fabs(tpar-ppar[kr-1]) < delta) ||
+	      (kr < ppar.size()-1 && fabs(tpar-ppar[kr+1]) < delta))
+	    remaining.push_back(std::make_pair(points[ki],tpar));
+	  else if (tpar < ppar[0]-delta)
+	    sorted[nsort].push_back(points[ki]);
+	  else if (tpar > ppar[ppar.size()-1]+delta)
+	    sorted[nsort+ppar.size()].push_back(points[ki]);
+	  else
+	    sorted[nsort+kr].push_back(points[ki]);
+	}
+    }
+ }
+
+BoundingBox bBoxGroup(vector<RevEngPoint*>& group)
+{
+  BoundingBox bbox(3);
+  for (size_t ki=0; ki<group.size(); ++ki)
+    {
+      Vector3D xyz = group[ki]->getPoint();
+      Point pnt(xyz[0], xyz[1], xyz[2]);
+      bbox.addUnionWith(pnt);
+    }
+  return bbox;
+}
+
+//===========================================================================
+void RevEng::defineSmallRegionSurfaces()
+//===========================================================================
+{
+  if (model_axis_.size() == 0)
+    return;
+  
+#ifdef DEBUG_SMALL
+  std::ofstream of1("nosurf_reg.g2");
+  std::ofstream of1_2("withsurf_reg.g2");
+  std::ofstream of1_3("assblend_reg.g2");
+#endif
+
+  vector<RevEngRegion*> nosf_reg;
+  for (size_t kr=0; kr<regions_.size(); ++kr)
+    {
+      if (regions_[kr]->hasSurface())
+	{
+#ifdef DEBUG_SMALL
+	  regions_[kr]->writeRegionPoints(of1_2);
+#endif
+	  continue;
+	}
+      if (regions_[kr]->hasAssociatedBlend())
+	{
+#ifdef DEBUG_SMALL
+	  regions_[kr]->writeRegionPoints(of1_3);
+#endif
+	  continue;
+	}
+#ifdef DEBUG_SMALL
+      regions_[kr]->writeRegionPoints(of1);
+#endif
+      nosf_reg.push_back(regions_[kr].get());
+    }
+
+  // Sort according to identified planes
+  double angtol = 5.0*anglim_;
+  int num_pt_lim = min_point_region_/3;
+  double axisang = 2.0*angtol;
+  double planeang = angtol;
+
+  double diag = bbox_.low().dist(bbox_.high());
+
+  // Count surfaces and points associated to each model axis
+  size_t num_model_axis = model_axis_.size();
+  vector<int> n_msurf(num_model_axis, 0), n_mpts(num_model_axis, 0);
+  for (size_t ki=0; ki<num_model_axis; ++ki)
+    {
+      n_msurf[ki] += ((int)model_axis_[ki].plane_loc_.size() +
+		      (int)model_axis_[ki].rotational_loc_.size());
+      for (size_t kj=0; kj<model_axis_[ki].plane_loc_.size(); ++kj)
+	n_mpts[ki] += model_axis_[ki].plane_loc_[kj].second;
+      for (size_t kj=0; kj<model_axis_[ki].rotational_loc_.size(); ++kj)
+	n_mpts[ki] += model_axis_[ki].rotational_loc_[kj].second;
+    }
+
+  double allnsurf = 0.0;
+  double allnpt = 0.0;
+  double lim1 = 0.5, lim2 = 0.25, lim3 = 0.1;
+  for (size_t ki=0; ki<num_model_axis; ++ki)
+    {
+      allnsurf += n_msurf[ki];
+      allnpt += n_mpts[ki];
+    }
+
+  vector<size_t> axis_split;
+  axis_split.push_back(0);
+  size_t kia;
+  for (kia=0; kia<n_msurf.size() && (double)n_msurf[kia] >= lim1*(double)allnsurf &&
+	 (double)n_mpts[kia] >= lim1*(double)allnpt; ++kia);
+  if (kia > axis_split[axis_split.size()-1])
+    axis_split.push_back(kia);
+ for (; kia<n_msurf.size() && (double)n_msurf[kia] >= lim2*(double)allnsurf &&
+	 (double)n_mpts[kia] >= lim2*(double)allnpt; ++kia);
+  if (kia > axis_split[axis_split.size()-1])
+    axis_split.push_back(kia);
+ for (; kia<n_msurf.size() && (double)n_msurf[kia] >= lim3*(double)allnsurf &&
+	 (double)n_mpts[kia] >= lim3*(double)allnpt; ++kia);
+  if (kia > axis_split[axis_split.size()-1])
+    axis_split.push_back(kia);
+  if (num_model_axis > axis_split[axis_split.size()-1])
+    axis_split.push_back(num_model_axis);
+  
+  for (size_t ax=1; ax<axis_split.size(); ++ax)
+    {
+      size_t ax1 = axis_split[ax-1];
+      size_t ax2 = axis_split[ax];
+      
+      // Extract points according to identified axes and differ between planar
+      // and rotational candidates
+      vector<Point> axis_dir(ax2-ax1);
+      for (size_t ki=ax1; ki<ax2; ++ki)
+	axis_dir[ki-ax1] = model_axis_[ki].axis_;
+
+      vector<vector<RevEngPoint*> > axis_group1(2*axis_dir.size());
+      vector<vector<RevEngPoint*> > axis_group2(axis_dir.size());
+      vector<RevEngPoint*> remaining;
+      for (size_t ki=0; ki<nosf_reg.size(); ++ki)
+	{
+	  vector<vector<RevEngPoint*> > curr_group1, curr_group2;
+	  vector<RevEngPoint*> curr_remaining;
+	  (void)nosf_reg[ki]->sortByAxis(axis_dir, approx_tol_, axisang, planeang,
+					 curr_group1, curr_group2, curr_remaining);
+	  for (size_t kr=0; kr<curr_group1.size(); ++kr)
+	    if (curr_group1[kr].size() > 0)
+	      axis_group1[kr].insert(axis_group1[kr].end(), curr_group1[kr].begin(),
+				     curr_group1[kr].end());
+	  for (size_t kr=0; kr<curr_group2.size(); ++kr)
+	    if (curr_group2.size() > 0)
+	      axis_group2[kr].insert(axis_group2[kr].end(), curr_group2[kr].begin(),
+				     curr_group2[kr].end());
+	  if (curr_remaining.size() > 0)
+	    remaining.insert(remaining.end(), curr_remaining.begin(), curr_remaining.end());
+	}
+
+#ifdef DEBUG_SMALL
+      std::ofstream of2("small_axis_sort.g2");
+      for (size_t kr=0; kr<axis_group1.size(); kr+=2)
+	{
+	  if (axis_group1[kr].size() > 0)
+	    {
+	      of2 << "400 1 0 4 0 255 0 255" << std::endl;
+	      of2 << axis_group1[kr].size() << std::endl;
+	      for (size_t kw=0; kw<axis_group1[kr].size(); ++kw)
+		of2 << axis_group1[kr][kw]->getPoint() << std::endl;
+	    }
+	  if (axis_group1[kr+1].size() > 0)
+	    {
+	      of2 << "400 1 0 4 100 155 0 255" << std::endl;
+	      of2 << axis_group1[kr+1].size() << std::endl;
+	      for (size_t kw=0; kw<axis_group1[kr+1].size(); ++kw)
+		of2 << axis_group1[kr+1][kw]->getPoint() << std::endl;
+	    }
+	}
+
+      for (size_t kr=0; kr<axis_group2.size(); ++kr)
+	{
+	  if (axis_group2[kr].size() > 0)
+	    {
+	      of2 << "400 1 0 4 255 0 0 255" << std::endl;
+	      of2 << axis_group2[kr].size() << std::endl;
+	      for (size_t kw=0; kw<axis_group2[kr].size(); ++kw)
+		of2 << axis_group2[kr][kw]->getPoint() << std::endl;
+	    }
+	}
+
+      if (remaining.size() > 0)
+	{
+	  of2 << "400 1 0 4 0 0 255 255" << std::endl;
+	  of2 << remaining.size() << std::endl;
+	  for (size_t kw=0; kw<remaining.size(); ++kw)
+	    of2 << remaining[kw]->getPoint() << std::endl;
+	}
+    
+#endif
+  
+      // Sort according to identified axis and planes
+      vector<SmallSurface> small_surf;
+      vector<shared_ptr<RevEngRegion> > small_sf_reg;
+      vector<shared_ptr<HedgeSurface> > small_sf_hedge;
+     for (size_t ki=ax1, ki2=0; ki<ax2; ++ki, ++ki2)
+	{
+	  if (n_mpts[ki] < min_point_region_)
+	    continue;
+	  size_t num_planes = model_axis_[ki].plane_loc_.size();
+	  Point axis = model_axis_[ki].axis_;
+	  vector<shared_ptr<Plane> > axis_planes;
+
+	  if (num_planes > 0)
+	    {
+	      axis_planes.resize(num_planes);
+	      for (size_t kr=0; kr<num_planes; ++kr)
+		axis_planes[kr] = shared_ptr<Plane>(new Plane(model_axis_[ki].plane_loc_[kr].first,
+							      axis));
+#ifdef DEBUG_SMALL
+	      std::ofstream of4("axis_planes.g2");
+	      for (size_t kr=0; kr<num_planes; ++kr)
+		{
+		  shared_ptr<Plane> tmp_plane(axis_planes[kr]->clone());
+		  tmp_plane->setParameterBounds(-0.5*diag, -0.5*diag, 0.5*diag, 0.5*diag);
+		  tmp_plane->writeStandardHeader(of4);
+		  tmp_plane->write(of4);
+		}
+#endif
+	    }
+	  int ix = -1;
+	  double minang = std::numeric_limits<double>::max();
+	  for (int kb=0; kb<3; ++kb)
+	    {
+	      double ang = mainaxis_[kb].angle(axis);
+	      ang = std::min(ang, M_PI-ang);
+	      if (ang < minang)
+		{
+		  minang = ang;
+		  ix = kb;
+		}
+	    }
+	  ix = (ix > 0) ? ix-1 : 2;
+	  Point Cx = mainaxis_[ix].cross(axis);
+	  Cx.normalize();
+	
+	  Point midp(0.0, 0.0, 0.0);
+	  vector<double> ppar;
+	  double delta = 0.0001;
+	  if (num_planes > 0)
+	    {
+	      double fac = 1.0/(double)(num_planes);
+	      for (size_t kj=0; kj<num_planes; ++kj)
+		midp += fac*model_axis_[ki].plane_loc_[kj].first;
+
+	      ppar.resize(num_planes);
+	      for (size_t kj=0; kj<num_planes; ++kj)
+		ppar[kj] = (model_axis_[ki].plane_loc_[kj].first - midp)*axis;
+
+	      // Sort locations along axis
+	      for (size_t kj=0; kj<ppar.size(); ++kj)
+		for (size_t kr=kj+1; kr<ppar.size(); ++kr)
+		  if (ppar[kr] < ppar[kj])
+		    {
+		      std::swap(ppar[kj], ppar[kr]);
+		      std::swap(axis_planes[kj], axis_planes[kr]);
+		    }
+	      delta = std::max(delta, 0.01*(ppar[ppar.size()-1]-ppar[0]));
+	    }
+	  
+	  size_t nsort = ppar.size()+1;
+	  vector<vector<RevEngPoint*> > planar_cand(2*nsort);
+	  vector<vector<RevEngPoint*> > rotational_cand(nsort);
+	  vector<pair<RevEngPoint*,double> > at_planes;
+	  double tmin, tmax;
+	  double all_min = std::numeric_limits<double>::max();
+	  double all_max = std::numeric_limits<double>::lowest();
+	  sortAlongAxis(axis_group1[2*ki2], midp, axis_dir[ki2], ppar, delta,
+			planar_cand, 0, at_planes, tmin, tmax);
+	  all_min = std::min(all_min, tmin);
+	  all_max = std::max(all_max, tmax);
+	  sortAlongAxis(axis_group1[2*ki2+1], midp, axis_dir[ki2], ppar, delta,
+			planar_cand, nsort, at_planes, tmin, tmax);
+	  all_min = std::min(all_min, tmin);
+	  all_max = std::max(all_max, tmax);
+	  sortAlongAxis(axis_group2[ki2], midp, axis_dir[ki2], ppar, delta,
+			rotational_cand, 0, at_planes, tmin, tmax);
+	  all_min = std::min(all_min, tmin);
+	  all_max = std::max(all_max, tmax);
+	  ppar.insert(ppar.begin(), all_min-delta);
+	  ppar.push_back(all_max+delta);
+
+#ifdef DEBUG_SMALL
+	  std::ofstream of14("at_planes.g2");
+	  of14 << "400 1 0 4 100 0 155 255" << std::endl;
+	  of14 << at_planes.size() << std::endl;
+	  for (size_t kw=0; kw<at_planes.size(); ++kw)
+	    of14 << at_planes[kw].first->getPoint() << std::endl;
+#endif
+
+	  int min_point_reg2 = std::max(min_point_region_/10, 200);
+	  int min_point_reg3 = std::max(min_point_region_/20, 200);
+	  for (size_t kr=0; kr<rotational_cand.size(); ++kr)
+	    {
+      
+#ifdef DEBUG_SMALL
+	      std::ofstream of3("small_plane_int.g2");
+	      if (planar_cand[kr].size() > 0)
+		{
+		  of3 << "400 1 0 4 0 255 0 255" << std::endl;
+		  of3 << planar_cand[kr].size() << std::endl;
+		  for (size_t kw=0; kw<planar_cand[kr].size(); ++kw)
+		    of3 << planar_cand[kr][kw]->getPoint() << std::endl;
+		}
+
+	      if (planar_cand[nsort+kr].size() > 0)
+		{
+		  of3 << "400 1 0 4 100 155 0 255" << std::endl;
+		  of3 << planar_cand[nsort+kr].size() << std::endl;
+		  for (size_t kw=0; kw<planar_cand[nsort+kr].size(); ++kw)
+		    of3 << planar_cand[nsort+kr][kw]->getPoint() << std::endl;
+		}
+
+	      if (rotational_cand[kr].size() > 0)
+		{
+		  of3 << "400 1 0 4 255 0 0 255" << std::endl;
+		  of3 << rotational_cand[kr].size() << std::endl;
+		  for (size_t kw=0; kw<rotational_cand[kr].size(); ++kw)
+		    of3 << rotational_cand[kr][kw]->getPoint() << std::endl;
+		}
+#endif
+	      // Start with rotational
+	      if (rotational_cand[kr].size() >= min_point_reg2) //num_pt_lim) //min_point_region_)
+		{
+		  // Group adjacent
+		  vector<vector<RevEngPoint*> > rot_groups;
+		  RevEngUtils::identifyConGroups(rotational_cand[kr], rot_groups);
+#ifdef DEBUG_SMALL
+		  std::ofstream of5("con_rotate.g2");
+		  for (size_t kh=0; kh<rot_groups.size(); ++kh)
+		    {
+		      of5 << "400 1 0 0" << std::endl;
+		      of5 << rot_groups[kh].size() << std::endl;
+		      for (size_t kv=0; kv<rot_groups[kh].size(); ++kv)
+			of5 << rot_groups[kh][kv]->getPoint() << std::endl;
+		    }
+#endif
+
+		  for (size_t kh=0; kh<rot_groups.size(); ++kh)
+		    {
+		      BoundingBox bb = bBoxGroup(rot_groups[kh]);
+		      Point high = bb.high();
+		      Point low = bb.low();
+		      for (size_t kv=0; kv<=model_axis_[ki].rotational_loc_.size(); ++kv)
+			{
+			  Point loc;
+			  if (kv < model_axis_[ki].rotational_loc_.size())
+			    loc = model_axis_[ki].rotational_loc_[kv].first;
+			  // Point low2 = loc + ((low-loc)*axis)*axis;
+			  // Point high2 = loc + ((high-loc)*axis)*axis;
+			  // if ((low-low2)*(high-high2) > 0.0 &&
+			  // 	  std::min(low.dist(low2), high.dist(high2)) > low.dist(high))
+			  // 	continue;   // Large distance between points and axis
+
+			  // Check if the group can be associated any of the existing
+			  // surfaces
+			  size_t kj;
+			  for (kj=0; kj<small_surf.size(); ++kj)
+			    {
+			      if ((int)ki != small_surf[kj].axis_ix_ ||
+				  (int)kr != small_surf[kj].lev_ix_ ||
+				  (int)kv != small_surf[kj].pos_ix_)
+				continue;  // Not the same axis or interval
+
+			      if (small_surf[kj].type_ != 3)
+				continue;  // Surface type  not compatible
+
+			      // Test distance
+			      int all_in = 0, all_in2 = 0;
+			      for (size_t kw=0; kw<small_surf[kj].surfs_.size(); ++kw)
+				{
+				  double maxdist, avdist;
+				  int num_in, num2_in;
+				  vector<pair<double, double> > dist_ang;
+				  shared_ptr<ParamSurface> sf = small_surf[kj].surfs_[kw];
+				  RevEngUtils::distToSurf(rot_groups[kh], sf,
+							  approx_tol_, angtol, maxdist, avdist,
+							  num_in, num2_in, dist_ang);
+				  all_in += num_in;
+				  all_in2 += num2_in;
+				}
+			      if (all_in2 >= std::max((int)rot_groups[kh].size()/2, 1))
+				break;  // Preliminary match found
+			    }
+
+			  if (kj < small_surf.size())
+			    {
+			      small_surf[kj].addPoints(rot_groups[kh], bb);
+			      break;
+			    }
+
+			  // Try to adapt a rotational surface
+			  if ((int)rot_groups[kh].size() < min_point_reg3)
+			    continue;
+
+			  vector<shared_ptr<ElementarySurface> > sfs;
+			  bool found = identifySmallRotational(rot_groups[kh], midp, loc,
+							       axis, Cx,
+							       ppar[kr], ppar[kr+1], sfs);
+			  if (found)
+			    {
+#ifdef DEBUG_SMALL
+			      std::ofstream of6("rot_sfs.g2");
+			      for (size_t kw=0; kw<sfs.size(); ++kw)
+				{
+				  sfs[kw]->writeStandardHeader(of6);
+				  sfs[kw]->write(of6);
+				}
+#endif
+		      
+			      SmallSurface small((int)ki, (int)kv, (int)kr, 3, sfs);
+			      small.addPoints(rot_groups[kh], bb);
+			      small_surf.push_back(small);
+			      break;
+			    }
+			  int stop_break = 1;
+			}
+		    }
+		}
+	  
+	      // Planar
+	      for (int ka=0; ka<2; ++ka)
+		{
+		  if (planar_cand[ka*nsort+kr].size() < min_point_reg2) //num_pt_lim) //min_point_region_)
+		    continue;
+
+		  // Group adjacent
+		  vector<vector<RevEngPoint*> > pla_groups;
+		  RevEngUtils::identifyConGroups(planar_cand[ka*nsort+kr], pla_groups);
+#ifdef DEBUG_SMALL
+		  std::ofstream of9("pla_groups.g2");
+		  for (size_t kh=0; kh<pla_groups.size(); ++kh)
+		    {
+		      of9 << "400 1 0 0" << std::endl;
+		      of9 << pla_groups[kh].size() << std::endl;
+		      for (size_t kv=0; kv<pla_groups[kh].size(); ++kv)
+			of9 << pla_groups[kh][kv]->getPoint() << std::endl;
+		    }
+#endif
+
+		  for (size_t kh=0; kh<pla_groups.size(); ++kh)
+		    {
+		      BoundingBox bb = bBoxGroup(pla_groups[kh]);
+		      Point high = bb.high();
+		      Point low = bb.low();
+		  
+		      // Check if the group can be associated any of the existing
+		      // surfaces
+		      size_t kj;
+		      for (kj=0; kj<small_surf.size(); ++kj)
+			{
+			  if ((int)ki != small_surf[kj].axis_ix_ ||
+			      (int)kr != small_surf[kj].lev_ix_)
+			    continue;  // Not the same axis or interval
+		      
+			  if (small_surf[kj].type_ != ka+1)
+			    continue;  // Surface type  not compatible
+
+			  // Test distance
+			  int all_in = 0, all_in2 = 0;
+			  for (size_t kw=0; kw<small_surf[kj].surfs_.size(); ++kw)
+			    {
+			      double maxdist, avdist;
+			      int num_in, num2_in;
+			      vector<pair<double, double> > dist_ang;
+			      shared_ptr<ParamSurface> sf = small_surf[kj].surfs_[kw];
+			      RevEngUtils::distToSurf(pla_groups[kh], sf,
+						      approx_tol_, angtol, maxdist, avdist,
+						      num_in, num2_in, dist_ang);
+			      all_in += num_in;
+			      all_in2 += num2_in;
+			    }
+			  if (all_in2 >= std::max((int)pla_groups[kh].size()/2, 1))
+			    break;  // Prelimenary match found
+			}
+		  
+		      if (kj < small_surf.size())
+			{
+			  small_surf[kj].addPoints(pla_groups[kh], bb);
+			  continue;
+			}
+		  
+		      // Try to adapt a planar surface
+		      if ((int)pla_groups[kh].size() < min_point_reg3)
+			continue;
+
+		      vector<shared_ptr<ElementarySurface> > sfs;
+		      bool found = identifySmallPlanar(pla_groups[kh], midp, axis, Cx,
+						       ppar[kr], ppar[kr+1], delta, sfs);
+		      if (found)
+			{
+#ifdef DEBUG_SMALL
+			  std::ofstream of9("pla_sfs.g2");
+			  for (size_t kw=0; kw<sfs.size(); ++kw)
+			    {
+			      sfs[kw]->writeStandardHeader(of9);
+			      sfs[kw]->write(of9);
+			    }
+#endif
+		      
+			  SmallSurface small((int)ki, -1, (int)kr, ka+1, sfs);
+			  small.addPoints(pla_groups[kh], bb);
+			  small_surf.push_back(small);
+			}
+		      int stop_break = 1;
+		    }
+		}
+#ifdef DEBUG_SMALL
+	      std::ofstream of8("small_sfs_curr.g2");
+	      for (size_t kv=0; kv<small_surf.size(); ++kv)
+		{
+		  if (small_surf[kv].lev_ix_ != (int)kr)
+		    continue;
+		  for (size_t kw=0; kw<small_surf[kv].surfs_.size(); ++kw)
+		    {
+		      small_surf[kv].surfs_[kw]->writeStandardHeader(of8);
+		      small_surf[kv].surfs_[kw]->write(of8);
+		    }
+		  for (size_t kw=0; kw<small_surf[kv].assos_points_.size(); ++kw)
+		    {
+		      of8 << "400 1 0 0" << std::endl;
+		      of8 << small_surf[kv].assos_points_[kw].size() << std::endl;
+		      for (size_t kw2=0; kw2<small_surf[kv].assos_points_[kw].size(); ++kw2)
+			of8 << small_surf[kv].assos_points_[kw][kw2]->getPoint() << std::endl;
+		    }
+		}
+#endif
+	      int stop_break3 = 1;
+	    }
+
+#ifdef DEBUG_SMALL
+	  std::ofstream of7("small_sfs_axis.g2");
+	  for (size_t kv=0; kv<small_surf.size(); ++kv)
+	    {
+	      for (size_t kw=0; kw<small_surf[kv].surfs_.size(); ++kw)
+		{
+		  small_surf[kv].surfs_[kw]->writeStandardHeader(of7);
+		  small_surf[kv].surfs_[kw]->write(of7);
+		}
+	      for (size_t kw=0; kw<small_surf[kv].assos_points_.size(); ++kw)
+		{
+		  of7 << "400 1 0 0" << std::endl;
+		  of7 << small_surf[kv].assos_points_[kw].size() << std::endl;
+		  for (size_t kw2=0; kw2<small_surf[kv].assos_points_[kw].size(); ++kw2)
+		    of7 << small_surf[kv].assos_points_[kw][kw2]->getPoint() << std::endl;
+		}
+	    }
+#endif
+	  // Associate near-plane points to existing planes or create new
+	  // planes as appopriate
+ 	  for (size_t kj=0; kj<axis_planes.size(); ++kj)
+	    {
+	      double tpar = ppar[kj+1];
+	      vector<RevEngPoint*> plane_pts;
+	      for (size_t kr=0; kr<at_planes.size(); ++kr)
+		if (fabs(at_planes[kr].second - tpar) <= delta)
+		  plane_pts.push_back(at_planes[kr].first);
+
+	      Point loc = axis_planes[kj]->location();
+	      Point norm = axis_planes[kj]->direction();
+	      vector<HedgeSurface*> hedge_surf;
+	      for (size_t kr=0; kr<surfaces_.size(); ++kr)
+		{
+		  shared_ptr<ParamSurface> surf = surfaces_[kr]->surface();
+		  if (surf->instanceType() != Class_Plane)
+		    continue;
+		  shared_ptr<Plane> plane = dynamic_pointer_cast<Plane,ParamSurface>(surf);
+		  Point loc2 = plane->location();
+		  Point norm2 = plane->direction();
+		  double ang = norm.angle(norm2);
+		  ang = std::min(ang, M_PI-ang);
+		  double dist = (loc2 - loc)*norm;
+		  if (ang <= angtol && fabs(dist) <= approx_tol_)
+		    hedge_surf.push_back(surfaces_[kr].get());
+		}
+#ifdef DEBUG_SMALL
+	      std::ofstream of15("at_curr_planes.g2");
+	      of15 << "400 1 0 4 0 255 0 255" << std::endl;
+	      of15 << plane_pts.size() << std::endl;
+	      for (size_t kv=0; kv<plane_pts.size(); ++kv)
+		of15 << plane_pts[kv]->getPoint() << std::endl;
+	      for (size_t kv=0; kv<hedge_surf.size(); ++kv)
+		{
+		  RevEngRegion *reg = hedge_surf[kv]->getRegion(0);
+		  reg->writeRegionPoints(of15);
+		  reg->writeSurface(of15);
+		}
+#endif
+	      size_t curr_nmb_small = small_sf_reg.size();
+	      planarAtPlane(axis_planes[kj], plane_pts, hedge_surf, small_sf_reg,
+			    small_sf_hedge);
+#ifdef DEBUG_SMALL
+	      std::ofstream of16("at_curr_planes2.g2");
+	      for (size_t kv=0; kv<hedge_surf.size(); ++kv)
+		{
+		  if (!hedge_surf[kv])
+		    continue;
+		  RevEngRegion *reg = hedge_surf[kv]->getRegion(0);
+		  reg->writeRegionPoints(of16);
+		  if (reg->hasSurface())
+		    reg->writeSurface(of16);
+		}
+	      for (size_t kv=curr_nmb_small; kv<small_sf_reg.size(); ++kv)
+		{
+		  small_sf_reg[kv]->writeRegionPoints(of16);
+		  small_sf_reg[kv]->writeSurface(of16);
+		}
+#endif
+	      
+	      int stop_plane = 0;
+	    }
+      
+	  int stop_break2 = 1;
+	}
+
+     vector<vector<RevEngPoint*> > remain_groups;
+     RevEngUtils::identifyConGroups(remaining, remain_groups);
+#ifdef DEBUG_SMALL
+     std::ofstream ofr("con_remain.g2");
+     for (size_t kh=0; kh<remain_groups.size(); ++kh)
+       {
+	 ofr << "400 1 0 0" << std::endl;
+	 ofr << remain_groups[kh].size() << std::endl;
+	 for (size_t kv=0; kv<remain_groups[kh].size(); ++kv)
+	   ofr << remain_groups[kh][kv]->getPoint() << std::endl;
+       }
+#endif
+
+     for (size_t kh=0; kh<remain_groups.size(); ++kh)
+       {
+	 if ((int)remain_groups[kh].size() <  num_pt_lim)
+	   continue;
+	 vector<RevEngPoint*> in_pts, out_pts;
+	 BoundingBox bb;
+	 shared_ptr<ElementarySurface> elem =
+	   defineElemSurf(remain_groups[kh], in_pts, bb, out_pts);
+	 if (elem.get())
+	   {
+	     vector<shared_ptr<ElementarySurface> > sfs;
+	     sfs.push_back(elem);
+#ifdef DEBUG_SMALL
+	     std::ofstream ofe("elem_remain_sf.g2");
+	     for (size_t kw=0; kw<sfs.size(); ++kw)
+	       {
+		 sfs[kw]->writeStandardHeader(ofe);
+		 sfs[kw]->write(ofe);
+	       }
+#endif
+		      
+	     SmallSurface small(-1, -1, -1, 4, sfs);
+	     small.addPoints(remain_groups[kh], bb);
+	     small_surf.push_back(small);
+	   }
+	 int stop_break_elem = 1;
+       }
+     
+
+     // Extract identified surfaces
+      vector<RevEngPoint*> non_assigned_pts;
+      for (size_t kj=0; kj<small_surf.size(); ++kj)
+	{
+	  extractSmallSurfs(small_surf[kj], small_sf_reg, small_sf_hedge,
+			    nosf_reg, non_assigned_pts);
+	}
+
+     // Remove empty regions
+      for (int ka=(int)nosf_reg.size()-1; ka>=0; --ka)
+	if (nosf_reg[ka]->numPoints() == 0)
+	  {
+	    for (int kb=(int)regions_.size()-1; kb>=0; --kb)
+	      if (regions_[kb].get() == nosf_reg[ka])
+		{
+		  regions_.erase(regions_.begin()+kb);
+		  break;
+		}
+	    nosf_reg.erase(nosf_reg.begin()+ka);
+	  }
+#ifdef DEBUG_SMALL
+      std::ofstream of10("all_small_sfs.g2");
+      for (size_t kj=0; kj<small_sf_reg.size(); ++kj)
+	{
+	  small_sf_reg[kj]->writeRegionPoints(of10);
+	  small_sf_reg[kj]->writeSurface(of10);
+	}
+      std::ofstream of11("nosurf_reg2.g2");
+      for (size_t kj=0; kj<nosf_reg.size(); ++kj)
+	nosf_reg[kj]->writeRegionPoints(of11);
+#endif
+
+      // Ensure connectivity of remaining small regions
+      vector<shared_ptr<RevEngRegion> > nosf_add;
+      size_t nmb_nosf = nosf_reg.size();
+     for (size_t kj=0; kj<nosf_reg.size(); ++kj)
+	{
+	  vector<vector<RevEngPoint*> > tmp_add;
+	  int classtype = nosf_reg[kj]->getClassificationType();
+	  nosf_reg[kj]->splitRegion(tmp_add);
+	  for (size_t kr=0; kr<tmp_add.size(); ++kr)
+	    {
+	      shared_ptr<RevEngRegion> tmp_reg(new RevEngRegion(classtype,
+								edge_class_type_,
+								tmp_add[kr]));
+	      nosf_add.push_back(tmp_reg);
+	    }
+	}
+
+      for (size_t kj=0; kj<nosf_add.size(); ++kj)
+	nosf_reg.push_back(nosf_add[kj].get());
+  
+#ifdef DEBUG_SMALL
+      std::ofstream of12("nosurf_reg3.g2");
+      for (size_t kj=0; kj<nosf_reg.size(); ++kj)
+	nosf_reg[kj]->writeRegionPoints(of12);
+#endif
+
+      vector<RevEngRegion*> include_reg;
+      integrateInSmallSurfs(small_sf_reg, nosf_reg, include_reg);
+      for (size_t kj=0; kj<include_reg.size(); ++kj)
+	{
+	  include_reg[kj]->removeFromAdjacent();
+	  include_reg[kj]->clearRegionAdjacency();
+	}
+      for (int ka=(int)include_reg.size()-1; ka>=0; --ka)
+	{
+	  auto it = std::find(nosf_reg.begin(), nosf_reg.end(), include_reg[ka]);
+	  if (it == nosf_reg.end())
+	    continue; // Should not happen
+	  int ix = it - nosf_reg.begin();
+	  if (ix >= (int)nmb_nosf)
+	    {
+	      // nosf_add[ix-(int)nmb_nosf]->removeFromAdjacent();
+	      // nosf_add[ix-(int)nmb_nosf]->clearRegionAdjacency();
+	      nosf_add.erase(nosf_add.begin()+ix-(int)nmb_nosf);
+	    }
+	  else
+	    {
+	      for (int kb=(int)regions_.size()-1; kb>=0; --kb)
+		if (regions_[kb].get() == include_reg[ka])
+		  {
+		    // regions_[kb]->removeFromAdjacent();
+		    // regions_[kb]->clearRegionAdjacency();
+		    regions_.erase(regions_.begin()+kb);
+		    break;
+		  }
+	      nmb_nosf--;
+	    }
+	  nosf_reg.erase(nosf_reg.begin()+ix);
+	}
+
+#ifdef DEBUG_SMALL
+      std::ofstream of13("all_small_sfs2.g2");
+      for (size_t kj=0; kj<small_sf_reg.size(); ++kj)
+	{
+	  small_sf_reg[kj]->writeRegionPoints(of13);
+	  small_sf_reg[kj]->writeSurface(of13);
+	}
+      std::ofstream of14("nosurf_reg4.g2");
+      for (size_t kj=0; kj<nosf_reg.size(); ++kj)
+	nosf_reg[kj]->writeRegionPoints(of14);
+#endif
+
+      if (small_sf_reg.size() > 0)
+	regions_.insert(regions_.end(), small_sf_reg.begin(), small_sf_reg.end());
+      if (nosf_add.size() > 0)
+	regions_.insert(regions_.end(), nosf_add.begin(), nosf_add.end());
+      if (small_sf_hedge.size() > 0)
+	surfaces_.insert(surfaces_.end(), small_sf_hedge.begin(), small_sf_hedge.end());
+    }
+}
+
+//===========================================================================
+struct FittingResults
+{
+  double maxd, avd;
+  int inside, inside_2;
+  vector<double> param;
+  vector<pair<double,double> > distang;
+};
+
+void collectAdjacentSurfRegs(vector<RevEngPoint*>& points,
+			     vector<pair<RevEngRegion*, int> >& adj_regs)
+{
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngRegion*> adj = points[ki]->adjacentRegsWithSurf();
+      for (size_t kj=0; kj<adj.size(); ++kj)
+	{
+	  size_t kr;
+	  for (kr=0; kr<adj_regs.size(); ++kr)
+	    if (adj_regs[kr].first == adj[kj])
+	      break;
+	  if (kr < adj_regs.size())
+	    adj_regs[kr].second++;
+	  else
+	    adj_regs.push_back(std::make_pair(adj[kj], 1));
+	}
+    }
+}
+
+double mostCompatibleDir(const Point& curr, vector<Point>& vecs1,
+			 vector<Point>& vecs2, Point& dir)
+{
+  double min_ang = M_PI;
+  for (size_t ki=0; ki<vecs1.size(); ++ki)
+    {
+      double ang = curr.angle(vecs1[ki]);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < min_ang)
+	{
+	  min_ang = ang;
+	  dir = vecs1[ki];
+	}
+    }
+  for (size_t ki=0; ki<vecs2.size(); ++ki)
+    {
+      double ang = curr.angle(vecs2[ki]);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < min_ang)
+	{
+	  min_ang = ang;
+	  dir = vecs2[ki];
+	}
+    }
+  return min_ang;
+}
+
+void identifyDistPoints(vector<RevEngPoint*>& points, vector<pair<double,double> >& distang,
+			double lim, vector<RevEngPoint*>& in_pts,
+			vector<RevEngPoint*>& out_pts)
+{
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      if (distang[ki].first <= lim)
+	in_pts.push_back(points[ki]);
+      else
+	out_pts.push_back(points[ki]);
+    }
+}
+
+shared_ptr<ElementarySurface>
+RevEng::defineElemSurf(vector<RevEngPoint*>& points,
+		       vector<RevEngPoint*>& in_points,
+		       BoundingBox& bbox, vector<RevEngPoint*>& remain)
+//===========================================================================
+{
+  shared_ptr<ElementarySurface> dummy_sf;
+  
+  // Collect direction information in adjacent surfaces
+  vector<pair<RevEngRegion*,int> > adj_sf_regs;
+  collectAdjacentSurfRegs(points, adj_sf_regs);
+
+  vector<Point> vecs1;
+  vector<int> num_reg_pts;
+  for (size_t ki=0; ki<adj_sf_regs.size(); ++ki)
+    {
+      shared_ptr<ParamSurface> surf =
+	adj_sf_regs[ki].first->getSurface(0)->surface();
+      shared_ptr<ElementarySurface> elem =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+      if (!elem.get())
+	continue;
+      vecs1.push_back(elem->direction());
+      num_reg_pts.push_back(adj_sf_regs[ki].first->numPoints());
+    }
+
+  vector<Point> vecs2(model_axis_.size());
+  vector<int> num_axis_pt(model_axis_.size(), 0);
+  for (size_t ki=0; ki<model_axis_.size(); ++ki)
+    {
+      vecs2[ki] = model_axis_[ki].axis_;
+      for (size_t kj=0; kj<model_axis_[ki].plane_loc_.size(); ++kj)
+	num_axis_pt[ki] += model_axis_[ki].plane_loc_[kj].second;
+      for (size_t kj=0; kj<model_axis_[ki].rotational_loc_.size(); ++kj)
+	num_axis_pt[ki] += model_axis_[ki].rotational_loc_[kj].second;
+    }
+
+  // Prepare points for surface generation
+  vector<RevEngPoint*> dummy_pts;
+  vector<pair<vector<RevEngPoint*>::iterator,vector<RevEngPoint*>::iterator> > pts_it;
+  pts_it.push_back(std::make_pair(points.begin(), points.end()));
+  vector<Point> pts(points.size());
+  bbox = BoundingBox(3);
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      pts[ki] = Point(xyz[0], xyz[1], xyz[2]);
+      bbox.addUnionWith(pts[ki]);
+    }
+  Point low = bbox.low();
+  Point high = bbox.high();
+  
+#ifdef DEBUG_SMALL
+  std::ofstream of("remaining_group.g2");
+  of << "400 1 0 4 55 100 100 255" << std::endl;
+  of << points.size() << std::endl;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    of << points[ki]->getPoint() << std::endl;
+#endif
+  // Collect information about feasible approximating surfaces
+  double eps = 1.0e-9;
+  double angtol = 5.0*anglim_;
+  double tol2 = 2.0*approx_tol_;
+  double avd_fac = 2.0;
+  double in_fac = 0.2;
+  double fac1 = 0.9;
+  double fac2 = 0.9;
+  double red_fac = 0.75;
+  double ang_lim2 = 0.25*M_PI;
+
+  vector<shared_ptr<ElementarySurface> > elem_sfs;
+  vector<int> flag;
+  vector<FittingResults> acc_fit;
+  vector<vector<RevEngPoint*> > used_pts;
+  vector<vector<RevEngPoint*> > not_used_pts;
+  
+  // Plane
+  Point init_norm = points[0]->getTriangNormal();
+  Point pos1, norm1, Cx1, Cy1;
+  RevEngUtils::computePlane(pts, init_norm, mainaxis_, pos1, norm1, Cx1, Cy1);
+  if (norm1.length() < 0.1)
+    return dummy_sf;   // The length should be one, but could be zero if the
+  // approximation fails
+  shared_ptr<Plane> plane1(new Plane(pos1, norm1, Cx1));
+
+#ifdef DEBUG_SMALL
+  plane1->writeStandardHeader(of);
+  plane1->write(of);
+#endif
+  FittingResults acc1;
+  RevEngUtils::distToSurf(points.begin(), points.end(), plane1, approx_tol_,
+			  acc1.maxd, acc1.avd, acc1.inside, acc1.inside_2,
+			  acc1.param, acc1.distang, angtol);
+  if (acc1.avd < avd_fac*approx_tol_ && (double)acc1.inside_2 > in_fac*(double)points.size())
+    {
+      int surf_flag1 = regions_[0]->defineSfFlag((int)points.size(), 0, approx_tol_,
+						 acc1.inside, acc1.inside_2,
+						 acc1.avd, false);
+      // Possible surface. Check with alternative plane normal
+      Point dir;
+      double ang = mostCompatibleDir(plane1->direction(), vecs1, vecs2, dir);
+      if (ang > eps && ang <= angtol)
+	{
+	  // Update plane with new normal
+	  shared_ptr<Plane> plane2 =
+	    RevEngUtils::planeWithAxis(points, dir, plane1->location(), mainaxis_);
+	  FittingResults acc2;
+	  RevEngUtils::distToSurf(points.begin(), points.end(), plane1, approx_tol_,
+				  acc2.maxd, acc2.avd, acc2.inside, acc2.inside_2,
+				  acc2.param, acc2.distang, angtol);
+	  int surf_flag2 = regions_[0]->defineSfFlag((int)points.size(), 0, approx_tol_,
+						     acc2.inside, acc2.inside_2,
+						     acc2.avd, false);
+ 	  if (surf_flag2 <= surf_flag1 && acc2.avd <= fac1*acc1.avd &&
+	      (double)acc2.inside_2 >= fac2*(double)acc1.inside_2)
+	    {
+#ifdef DEBUG_SMALL
+	      plane2->writeStandardHeader(of);
+	      plane2->write(of);
+#endif
+	      std::swap(acc1, acc2);
+	      std::swap(plane1, plane2);
+	    }
+	}
+
+      bool use_reduced = false;
+      if (acc1.maxd > tol2)
+	{
+	  // Remove most distant points and make a refit
+	  vector<RevEngPoint*> in_pts, out_pts;
+	  identifyDistPoints(points, acc1.distang, tol2, in_pts, out_pts);
+	  if ((double)in_pts.size() > red_fac*(double)points.size())
+	    {
+	      vector<Point> in_pts2(in_pts.size());
+	      for (size_t ki=0; ki<in_pts.size(); ++ki)
+		{
+		  Vector3D xyz = in_pts[ki]->getPoint();
+		  in_pts2[ki] = Point(xyz[0], xyz[1], xyz[2]);
+		}
+	      Point pos2, norm2, Cx2, Cy2;
+	      RevEngUtils::computePlane(in_pts2, plane1->direction(), mainaxis_, pos2,
+					norm2, Cx2, Cy2);
+	      shared_ptr<Plane> plane2(new Plane(pos2, norm2, Cx2));
+	  
+	      FittingResults acc2;
+	      RevEngUtils::distToSurf(in_pts.begin(), in_pts.end(), plane2, approx_tol_,
+				      acc2.maxd, acc2.avd, acc2.inside, acc2.inside_2,
+				      acc2.param, acc2.distang, angtol);
+	      int surf_flag2 = regions_[0]->defineSfFlag((int)in_pts.size(), 0, approx_tol_,
+							 acc2.inside, acc2.inside_2,
+							 acc2.avd, false);
+	      double frac1 = (double)acc1.inside_2/(double)points.size();
+	      double frac2 = (double)acc2.inside_2/(double)in_pts.size();
+	      if (surf_flag2 < ACCURACY_POOR &&
+		  surf_flag2 <= surf_flag1 && acc2.avd < acc1.avd && frac2 > frac1)
+		{
+#ifdef DEBUG_SMALL
+		  plane2->writeStandardHeader(of);
+		  plane2->write(of);
+#endif
+		  use_reduced = true;
+		  elem_sfs.push_back(plane2);
+		  flag.push_back(surf_flag2);
+		  acc_fit.push_back(acc2);
+		  used_pts.push_back(in_pts);
+		  not_used_pts.push_back(out_pts);
+		}
+	    }
+	}
+      if (surf_flag1 < ACCURACY_POOR && (!use_reduced))
+	{
+	  elem_sfs.push_back(plane1);
+	  flag.push_back(surf_flag1);
+	  acc_fit.push_back(acc1);
+	  used_pts.push_back(points);
+	  not_used_pts.push_back(dummy_pts);
+	}
+    }
+
+  // Cylinder and cone
+  Point axis2, Cx2, Cy2, pos2;
+  double rad2;
+  RevEngUtils::computeAxis(pts_it, axis2, Cx2, Cy2);
+  RevEngUtils::computeCylPosRadius(pts_it, low, high, axis2, Cx2, Cy2, pos2, rad2);
+  shared_ptr<ElementarySurface> csf1(new Cylinder(rad2, pos2, axis2, Cx2));
+#ifdef DEBUG_SMALL
+  csf1->writeStandardHeader(of);
+  csf1->write(of);
+#endif
+  
+  FittingResults acc2;
+  RevEngUtils::distToSurf(points.begin(), points.end(), csf1, approx_tol_,
+			  acc2.maxd, acc2.avd, acc2.inside, acc2.inside_2,
+			  acc2.param, acc2.distang, angtol);
+
+  shared_ptr<Cone> cone =
+    RevEngUtils::coneWithAxis(points, csf1->direction(), low, high, mainaxis_);
+  if (fabs(cone->getConeAngle()) > angtol)
+    {
+      FittingResults acc3;
+      RevEngUtils::distToSurf(points.begin(), points.end(), cone, approx_tol_,
+			  acc3.maxd, acc3.avd, acc3.inside, acc3.inside_2,
+			  acc3.param, acc3.distang, angtol);
+      if (acc3.avd < acc2.avd && acc3.inside_2 > acc2.inside_2)
+	{
+#ifdef DEBUG_SMALL
+	  cone->writeStandardHeader(of);
+	  cone->write(of);
+#endif
+	  std::swap(acc2, acc3);
+	  csf1 = cone;
+	}
+    }
+      
+  if (acc2.avd < avd_fac*approx_tol_ && (double)acc2.inside_2 > in_fac*(double)points.size())
+    {
+      int surf_flag2 = regions_[0]->defineSfFlag((int)points.size(), 0, approx_tol_,
+						 acc2.inside, acc2.inside_2,
+						 acc2.avd, true);
+      
+      // Possible surface. Check with alternative plane normal
+      Point dir;
+      double ang = mostCompatibleDir(csf1->direction(), vecs1, vecs2, dir);
+      if (ang > eps && ang < ang_lim2)
+	{
+	  // Update rotational surface with new axis
+	  shared_ptr<ElementarySurface> csf2 =
+	    RevEngUtils::cylinderWithAxis(points, dir, low, high, mainaxis_);
+	  
+	  FittingResults acc3;
+	  RevEngUtils::distToSurf(points.begin(), points.end(), csf2, approx_tol_,
+				  acc3.maxd, acc3.avd, acc3.inside, acc3.inside_2,
+				  acc3.param, acc3.distang, angtol);
+ 				  
+	  shared_ptr<Cone> cone2 =
+	    RevEngUtils::coneWithAxis(points, csf2->direction(), low, high, mainaxis_);
+	  if (fabs(cone2->getConeAngle()) > angtol)
+	    {
+	      FittingResults acc4;
+	      RevEngUtils::distToSurf(points.begin(), points.end(), cone, approx_tol_,
+				      acc4.maxd, acc4.avd, acc4.inside, acc4.inside_2,
+				      acc4.param, acc4.distang, angtol);
+	      if (acc4.avd < acc3.avd && acc4.inside_2 > acc3.inside_2)
+		{
+		  std::swap(acc3, acc4);
+		  csf2 = cone2;
+		}
+	    }
+	  
+	  int surf_flag3 = regions_[0]->defineSfFlag((int)points.size(), 0, approx_tol_,
+						     acc3.inside, acc3.inside_2,
+						     acc3.avd, true);
+ 	  if (surf_flag3 <= surf_flag2 && acc3.avd <= fac1*acc2.avd &&
+	      (double)acc3.inside_2 >= fac2*(double)acc2.inside_2)
+	    {
+#ifdef DEBUG_SMALL
+	      csf2->writeStandardHeader(of);
+	      csf2->write(of);
+#endif
+	      std::swap(acc2, acc3);
+	      std::swap(csf1, csf2);
+	    }
+	}
+
+      bool use_reduced = false;
+      if (acc2.maxd > tol2)
+	{
+	  // Remove most distant points and make a refit
+	  vector<RevEngPoint*> in_pts, out_pts;
+	  identifyDistPoints(points, acc2.distang, tol2, in_pts, out_pts);
+	  if ((double)in_pts.size() > red_fac*(double)points.size())
+	    {
+	      vector<pair<vector<RevEngPoint*>::iterator,vector<RevEngPoint*>::iterator> > pts_it2;
+	      pts_it2.push_back(std::make_pair(in_pts.begin(), in_pts.end()));
+	      Point axis3, Cx3, Cy3, pos3;
+	      double rad3;
+	      RevEngUtils::computeAxis(pts_it2, axis3, Cx3, Cy3);
+	      RevEngUtils::computeCylPosRadius(pts_it2, low, high, axis3, Cx3, Cy3,
+					       pos3, rad3);
+	      shared_ptr<ElementarySurface> csf2(new Cylinder(rad3, pos3, axis3, Cx3));
+	      
+	  
+	      FittingResults acc3;
+	      RevEngUtils::distToSurf(in_pts.begin(), in_pts.end(), csf2, approx_tol_,
+				      acc3.maxd, acc3.avd, acc3.inside, acc3.inside_2,
+				      acc3.param, acc3.distang, angtol);
+	      shared_ptr<Cone> cone2 =
+		RevEngUtils::coneWithAxis(in_pts, csf2->direction(), low, high, mainaxis_);
+	      if (fabs(cone2->getConeAngle()) > angtol)
+		{
+		  FittingResults acc4;
+		  RevEngUtils::distToSurf(in_pts.begin(), in_pts.end(), cone2, approx_tol_,
+				      acc4.maxd, acc4.avd, acc4.inside, acc4.inside_2,
+				      acc4.param, acc4.distang, angtol);
+		  if (acc4.avd < acc3.avd && acc4.inside_2 > acc3.inside_2)
+		    {
+		      std::swap(acc3, acc4);
+		      csf2 = cone2;
+		    }
+		}
+	      
+	      int surf_flag3 = regions_[0]->defineSfFlag((int)in_pts.size(), 0, approx_tol_,
+							 acc3.inside, acc3.inside_2,
+							 acc3.avd, true);
+	      double frac1 = (double)acc2.inside_2/(double)points.size();
+	      double frac2 = (double)acc3.inside_2/(double)in_pts.size();
+	      if (surf_flag3 < ACCURACY_POOR &&
+		  surf_flag3 <= surf_flag2 && acc3.avd < acc2.avd && frac2 > frac1)
+		{
+#ifdef DEBUG_SMALL
+		  csf2->writeStandardHeader(of);
+		  csf2->write(of);
+#endif
+		  use_reduced = true;
+		  elem_sfs.push_back(csf2);
+		  flag.push_back(surf_flag3);
+		  acc_fit.push_back(acc3);
+		  used_pts.push_back(in_pts);
+		  not_used_pts.push_back(out_pts);
+		}
+	    }
+	}
+      if (surf_flag2 < ACCURACY_POOR && (!use_reduced))
+	{
+	  elem_sfs.push_back(csf1);
+	  flag.push_back(surf_flag2);
+	  acc_fit.push_back(acc2);
+	  used_pts.push_back(points);
+	  not_used_pts.push_back(dummy_pts);
+	}
+    }
+
+  // Sphere
+  int ix = 0;
+  for (size_t ki=1; ki<num_axis_pt.size(); ++ki)
+    if (num_axis_pt[ki] > num_axis_pt[ix])
+      ix = (int)ki;
+  Point axis3 = vecs2[ix];
+
+  shared_ptr<Sphere> sph1 = RevEngUtils::sphereWithAxis(points, axis3, mainaxis_);
+  FittingResults acc3;
+  RevEngUtils::distToSurf(points.begin(), points.end(), sph1, approx_tol_,
+			  acc3.maxd, acc3.avd, acc3.inside, acc3.inside_2,
+			  acc3.param, acc3.distang, angtol);
+  
+#ifdef DEBUG_SMALL
+  sph1->writeStandardHeader(of);
+  sph1->write(of);
+#endif
+  if (acc3.avd < avd_fac*approx_tol_ && (double)acc3.inside_2 > in_fac*(double)points.size())
+    {
+      int surf_flag3 = regions_[0]->defineSfFlag((int)points.size(), 0, approx_tol_,
+						 acc3.inside, acc3.inside_2,
+						 acc3.avd, false);
+      bool use_reduced = false;
+      if (acc3.maxd > tol2)
+	{
+	  // Remove most distant points and make a refit
+	  vector<RevEngPoint*> in_pts, out_pts;
+	  identifyDistPoints(points, acc3.distang, tol2, in_pts, out_pts);
+	  if ((double)in_pts.size() > red_fac*(double)points.size())
+	    {
+	      shared_ptr<Sphere> sph2 = RevEngUtils::sphereWithAxis(in_pts, axis3, mainaxis_);
+	      FittingResults acc4;
+	      RevEngUtils::distToSurf(in_pts.begin(), in_pts.end(), sph2, approx_tol_,
+				      acc4.maxd, acc4.avd, acc4.inside, acc4.inside_2,
+				      acc4.param, acc4.distang, angtol);
+	      int surf_flag4 = regions_[0]->defineSfFlag((int)in_pts.size(), 0, approx_tol_,
+							 acc3.inside, acc3.inside_2,
+							 acc3.avd, false);
+	      double frac1 = (double)acc3.inside_2/(double)points.size();
+	      double frac2 = (double)acc4.inside_2/(double)in_pts.size();
+	      if (surf_flag4 < ACCURACY_POOR &&
+		  surf_flag4 <= surf_flag3 && acc4.avd < acc3.avd && frac2 > frac1)
+		{
+#ifdef DEBUG_SMALL
+		  sph2->writeStandardHeader(of);
+		  sph2->write(of);
+#endif
+		  use_reduced = true;
+		  elem_sfs.push_back(sph2);
+		  flag.push_back(surf_flag4);
+		  acc_fit.push_back(acc4);
+		  used_pts.push_back(in_pts);
+		  not_used_pts.push_back(out_pts);
+		}
+	    }
+	}
+      if (surf_flag3 < ACCURACY_POOR && (!use_reduced))
+	{
+	  elem_sfs.push_back(sph1);
+	  flag.push_back(surf_flag3);
+	  acc_fit.push_back(acc3);
+	  used_pts.push_back(points);
+	  not_used_pts.push_back(dummy_pts);
+	}
+    }
+
+  // Select result
+  ix = -1;
+  int sf_flag = NOT_SET;
+  double inside_fac = 0.1;
+  for (size_t ki=0; ki<elem_sfs.size(); ++ki)
+    {
+      if ((double)acc_fit[ki].inside < inside_fac*(double)used_pts[ki].size())
+	continue;
+      if (flag[ki] < sf_flag)
+	{
+	  ix = (int)ki;
+	  sf_flag = flag[ki];
+	}
+      else if ((flag[ki] == sf_flag ||
+		(std::min(flag[ki],sf_flag) == 1 && std::max(flag[ki],sf_flag) == 2)) &&
+	       acc_fit[ki].avd < acc_fit[ix].avd &&
+	       acc_fit[ki].inside_2 > acc_fit[ix].inside_2)
+	{
+	  ix = (int)ki;
+	  sf_flag = flag[ki];
+	}
+    }
+      
+  if (ix >= 0)
+    {
+      in_points = used_pts[ix];
+      remain = not_used_pts[ix];
+      return elem_sfs[ix];
+    }
+  else
+    return dummy_sf;
+}
+
+//===========================================================================
+void RevEng::planarAtPlane(shared_ptr<Plane> axis_plane,
+			   vector<RevEngPoint*>& points,
+			   vector<HedgeSurface*>& sfs,
+			   vector<shared_ptr<RevEngRegion> >& plane_sf_reg,
+			   vector<shared_ptr<HedgeSurface> >& plane_sf_hedge)
+//===========================================================================
+{
+  if (sfs.size() == 0 || points.size() == 0)
+    return;
+
+  RevEngRegion *reg0 = sfs[0]->getRegion(0);
+  Point axis = axis_plane->direction();
+  Point loc = axis_plane->location();
+  int min_num_pts = min_point_region_/5;
+  double int_tol = 1.0e-4;
+
+  // Check accuracy with respect to axis plane
+  double angtol = 5.0*anglim_;
+  double maxdist, avdist;
+  int num_in, num2_in;
+  vector<double> parvals;
+  vector<pair<double,double> > distang;
+  vector<RevEngPoint*> in, out;
+  RevEngUtils::distToSurf(points.begin(), points.end(), axis_plane, approx_tol_,
+			  maxdist, avdist, num_in, num2_in, in, out,
+			  parvals, distang, angtol);
+  int sf_flag = reg0->defineSfFlag((int)points.size(), 0, approx_tol_, num_in,
+				  num2_in, avdist, false);
+
+  vector<bool> same_plane(sfs.size(), true);
+  double dtol = std::min(1.0e-4, 0.1*approx_tol_);
+  double atol = 0.001;
+  for (size_t ki=0; ki<sfs.size(); ++ki)
+    {
+      shared_ptr<ParamSurface> surf = sfs[ki]->surface();
+      shared_ptr<Plane> plane = dynamic_pointer_cast<Plane,ParamSurface>(surf);
+      if (!plane.get())
+	continue;
+      double ang = plane->direction().angle(axis);
+      ang = std::min(ang, M_PI-ang);
+      double dd = (plane->location() - loc)*axis;
+      if (ang > atol || dd > dtol)
+	same_plane[ki] = false;
+    }
+
+  // Check accuracy with respect to existing planar surfaces
+  vector<int> flag_sfs(sfs.size(), sf_flag);
+  vector<int> flag_axis(sfs.size(), NOT_SET);
+  vector<bool> has_revedgs(sfs.size(), false);
+  vector<int> num_points(sfs.size());
+  vector<RevEngRegion*> sfs_reg(sfs.size());
+  for (size_t ki=0; ki<sfs.size(); ++ki)
+    {
+      RevEngRegion *reg = sfs[ki]->getRegion(0);
+      sfs_reg[ki] = reg;
+      num_points[ki] = reg->numPoints();
+      has_revedgs[ki] = reg->hasRevEdges();
+      if (same_plane[ki])
+	{
+	  flag_axis[ki] = reg->getSurfaceFlag();
+	  continue;
+	}
+      
+      shared_ptr<ParamSurface> surf = sfs[ki]->surface();
+      shared_ptr<Plane> plane = dynamic_pointer_cast<Plane,ParamSurface>(surf);
+      if (!plane.get())
+	continue;
+      // To avoid bounded plane
+      shared_ptr<Plane> plane2(new Plane(plane->location(), plane->direction()));
+      double maxd, avd;
+      int inside, inside2;
+      vector<double> param;
+      vector<pair<double,double> > d_a;
+      vector<RevEngPoint*> in0, out0;
+      RevEngUtils::distToSurf(points.begin(), points.end(), plane2, approx_tol_,
+			      maxd, avd, inside, inside2, in0, out0,
+			      param, d_a, angtol);
+      flag_sfs[ki] = reg0->defineSfFlag((int)points.size(), 0, approx_tol_, inside,
+					inside2, avd, false);
+      if (sf_flag >= ACCURACY_POOR && flag_sfs[ki] >= ACCURACY_POOR)
+	continue;  // Not compatible
+
+      if (sf_flag < ACCURACY_POOR)
+	{
+	  double maxd2, avd2;
+	  int inside_2, inside2_2;
+	  vector<double> param2;
+	  vector<pair<double,double> > d_a2;
+	  vector<RevEngPoint*> in2, out2;
+	  RevEngUtils::distToSurf(reg->pointsBegin(), reg->pointsEnd(), axis_plane,
+				  approx_tol_, maxd2, avd2, inside_2, inside2_2, in2, out2,
+				  param2, d_a2, angtol);
+	  flag_axis[ki] = reg->defineSfFlag(0, approx_tol_, inside_2,
+					    inside2_2, avd2, false);
+	  int stop_break = 1;
+	}
+      int stop_break2 = 1;
+    }
+
+  // Check if the planar points can be integrated in any of the existing surfaces
+  int ix = -1;
+  for (size_t ki=0; ki<sfs.size(); ++ki)
+    {
+      if (flag_sfs[ki] < ACCURACY_POOR &&
+	  (ix < 0 || flag_sfs[ki] < flag_sfs[ix] ||
+	   (flag_sfs[ki] == flag_sfs[ix] && has_revedgs[ki] && (!has_revedgs[ix])) ||
+	   (flag_sfs[ki] == flag_sfs[ix] && has_revedgs[ki] == has_revedgs[ix] &&
+	    num_points[ki] > num_points[ix])))
+	ix = ki;
+    }
+
+  if (ix < 0 && (sf_flag >= ACCURACY_POOR || (int)points.size() < min_num_pts))
+    return;
+
+  // Collect all points and parameterize on selected surface
+  shared_ptr<ParamSurface> sel_sf = (ix < 0) ? axis_plane : sfs[ix]->surface();
+  vector<RevEngPoint*> all_pts;
+  for (size_t ki=0; ki<sfs.size(); ++ki)
+    if (flag_axis[ki] < ACCURACY_POOR)
+      all_pts.insert(all_pts.end(), sfs_reg[ki]->pointsBegin(),
+		     sfs_reg[ki]->pointsEnd());
+  all_pts.insert(all_pts.end(), points.begin(), points.end());
+  double maxd_all, avd_all;
+  int num_in_all, num2_in_all;
+  vector<double> parvals_all;
+  vector<pair<double,double> > distang_all;
+  vector<RevEngPoint*> in_all, out_all;
+  RevEngUtils::distToSurf(all_pts.begin(), all_pts.end(), sel_sf, approx_tol_,
+			  maxd_all, avd_all, num_in_all, num2_in_all, in_all,
+			  out_all, parvals_all, distang_all, angtol);
+  int sf_flag_all = reg0->defineSfFlag((int)all_pts.size(), 0, approx_tol_, 
+				       num_in_all, num2_in_all, avd_all, false);
+  if (sf_flag_all >= ACCURACY_POOR)
+    return;  // Do nothing
+
+  // Assign parameter value and accuracy to points. This might have to be redone
+  for (size_t ki=0; ki<all_pts.size(); ++ki)
+    {
+      all_pts[ki]->setPar(Vector2D(parvals_all[2*ki], parvals_all[2*ki+1]));
+      all_pts[ki]->setSurfaceDist(distang_all[ki].first, distang_all[ki].second);
+    }
+  
+  vector<vector<RevEngPoint*> > con_all;
+  RevEngUtils::identifyConGroups(all_pts, con_all);
+#ifdef DEBUG_SMALL
+  std::ofstream of1("sep_pts_all.g2");
+  for (size_t ki=0; ki<con_all.size(); ++ki)
+    {
+      of1 << "400 1 0 0" << std::endl;
+      of1 << con_all[ki].size() << std::endl;
+      for (size_t kj=0; kj<con_all[ki].size(); ++kj)
+	of1 << con_all[ki][kj]->getPoint() << std::endl;
+    }
+#endif
+
+  for (size_t ki=0; ki<con_all.size(); ++ki)
+    {
+      // Assemble accuracy information
+      double maxdc = 0.0, avdc = 0.0;
+      int num_inc = 0, num2_inc = 0, ang_in = 0;
+      double fac = 1.0/(double)con_all[ki].size();
+      for (size_t kj=0; kj<con_all[ki].size(); ++kj)
+	{
+	  double dd, ang;
+	  con_all[ki][kj]->getSurfaceDist(dd, ang);
+	  maxdc = std::max(maxdc, dd);
+	  avdc += fac*dd;
+	  if (dd <= approx_tol_ && ang <= angtol)
+	    num_inc++;
+	  if (dd <= approx_tol_)
+	    num2_inc++;
+	  if (ang <= angtol)
+	    ang_in++;
+	}
+      int flagc = reg0->defineSfFlag((int)con_all[ki].size(), 0, approx_tol_,
+				     num_inc, num2_inc, avdc, false);
+      int min_nmb = std::max((int)con_all[ki].size()/4, 10);
+      if (ang_in < min_nmb && num_inc < min_nmb)
+	flagc = ACCURACY_POOR;
+      if (flagc < ACCURACY_POOR && (int)con_all[ki].size() > min_num_pts/2)
+	{
+	  // Check connection to existing surface
+	  vector<int> num_sf_points(sfs.size(), 0);
+	  for (size_t kj=0; kj<con_all[ki].size(); ++kj)
+	    {
+	      RevEngRegion *curr_reg = con_all[ki][kj]->region();
+	      for (size_t kr=0; kr<sfs.size(); ++kr)
+		if (curr_reg == sfs_reg[kr])
+		  {
+		    num_sf_points[kr]++;
+		    break;
+		  }
+	    }
+
+	  // Decide on corresponding surface
+	  int ix2 = -1;
+	  int num_corr = 0;
+	  for (size_t kr=0; kr<num_sf_points.size(); ++kr)
+	    if (num_sf_points[kr] > num_corr)
+	      {
+		ix2 = (int)kr;
+		num_corr = num_sf_points[kr];
+	      }
+
+	  // Remove the associated points from their previous region
+	  // Collect points with respect to regions
+	  vector<vector<RevEngPoint*> > reg_points;
+	  for (size_t kj=0; kj<con_all[ki].size(); ++kj)
+	    {
+	      RevEngRegion *curreg = con_all[ki][kj]->region();
+	      if ((ix2 >=0 && curreg != sfs_reg[ix2]) || ix2 < 0)
+		{
+		  size_t kr;
+		  for (kr=0; kr<reg_points.size(); ++kr)
+		      if (reg_points[kr][0]->region() == curreg)
+			break;
+		    if (kr < reg_points.size())
+		      reg_points[kr].push_back(con_all[ki][kj]);
+		    else
+		      {
+			vector<RevEngPoint*> curr_reg_pt;
+			curr_reg_pt.push_back(con_all[ki][kj]);
+			reg_points.push_back(curr_reg_pt);
+		      }
+		}
+	    }
+	  for (size_t kr=0; kr<reg_points.size(); ++kr)
+	    {
+	      RevEngRegion *currreg = reg_points[kr][0]->region();
+	      currreg->removePoints(reg_points[kr]);
+	      for (size_t kh=0; kh<reg_points[kr].size(); ++kh)
+		reg_points[kr][kh]->unsetRegion();
+	    }
+
+	  if (ix2 < 0)
+	    {
+	      // Define new region and surface
+	      shared_ptr<RevEngRegion> plane_reg(new RevEngRegion(reg0->getClassificationType(),
+								  edge_class_type_,
+								  con_all[ki]));
+	      shared_ptr<Plane> plane2(axis_plane->clone());
+	      shared_ptr<HedgeSurface> hedge(new HedgeSurface(sel_sf, plane_reg.get()));
+	      plane_reg->setHedge(hedge.get());
+	      plane_reg->updateInfo(approx_tol_, angtol);
+	      plane_reg->setSurfaceFlag(flagc);
+	      plane_sf_reg.push_back(plane_reg);
+	      plane_sf_hedge.push_back(hedge);
+	    }
+	  else
+	    {
+	      // Enhance region with added points
+	      vector<RevEngPoint*> added_pts;
+	      for (size_t kj=0; kj<con_all[ki].size(); ++kj)
+		{
+		  if (con_all[ki][kj]->region() != sfs_reg[ix2])
+		    added_pts.push_back(con_all[ki][kj]);
+		}
+	      if (added_pts.size() > 0)
+		{
+		  sfs_reg[ix2]->addPointsToGroup(added_pts, approx_tol_, angtol, false);
+		  vector<RevEngEdge*> rev_edgs = sfs_reg[ix2]->getAllRevEdges();
+		  for (size_t kj=0; kj<rev_edgs.size(); ++kj)
+		    rev_edgs[kj]->increaseExtendCount();
+		}
+	      if (ix2 != ix)
+		{
+		  // Reset parameterization
+		  sfs_reg[ix2]->parameterizePoints(approx_tol_, angtol);
+		}
+	    }
+	  int stop_break5 = 1;
+	}
+      else
+	{
+	  // Reset current parameterization, if any
+	  double eps = 1.0e-6;
+	  double upar, vpar, dist;
+	  Point close;
+	  Point norm1, norm2, norm3;
+	  double ang, ang2;
+	  for (size_t kj=0; kj<con_all[ki].size(); ++kj)
+	    {
+	      RevEngPoint *currpt = con_all[ki][kj];
+	      RevEngRegion *curreg = currpt->region();
+	      if (curreg->hasSurface())
+		{
+		  shared_ptr<ParamSurface> surf = curreg->getSurface(0)->surface();
+		  Vector3D xyz = currpt->getPoint();
+		  Point pnt(xyz[0], xyz[1], xyz[2]);
+		  surf->closestPoint(pnt, upar, vpar, close, dist, eps);
+		  surf->normal(norm1, upar, vpar);
+		  norm2 = currpt->getLocFuncNormal();
+		  norm3 = currpt->getTriangNormal();
+		  ang = norm1.angle(norm2);
+		  ang2 = norm1.angle(norm3);
+		  ang = std::min(std::min(M_PI-ang, ang), std::min(M_PI-ang2,ang2));
+		  currpt->setPar(Vector2D(upar, vpar));
+		  currpt->setSurfaceDist(dist, ang);
+		}
+	    }
+	}
+      int stop_break4 = 1;
+    }
+
+  vector<vector<RevEngPoint*> > separate_groups;
+  vector<HedgeSurface*> removed_surfs;
+  vector<RevEngEdge*> removed_edgs;
+  for (size_t ki=0; ki<sfs_reg.size(); ++ki)
+    {
+     if (sfs_reg[ki]->numPoints() == 0)
+	{
+	  vector<RevEngEdge*> rev_edgs = sfs_reg[ki]->getAllRevEdges();
+	  for (size_t kr=0; kr<rev_edgs.size(); ++kr)
+	    {
+	      size_t kj;
+	      for (kj=0; kj<edges_.size(); ++kj)
+		if (edges_[kj].get() == rev_edgs[kr])
+		  break;
+	      if (kj < edges_.size())
+		edges_.erase(edges_.begin()+kj);
+	    }
+	  
+	  // Remove from region pool
+	  for (size_t kr=0; kr<regions_.size(); ++kr)
+	    if (regions_[kr].get() == sfs_reg[ki])
+	      {
+		regions_[kr]->removeFromAdjacent();
+		regions_[kr]->clearRegionAdjacency();
+		regions_.erase(regions_.begin()+kr);
+		break;
+	      }
+	  removed_surfs.push_back(sfs[ki]);
+	  sfs[ki] = 0;
+	}
+     else
+       {
+	 // Split region if not connected
+	 sfs_reg[ki]->splitRegion(separate_groups);
+	 if (sfs_reg[ki]->numPoints() < min_num_pts)
+	   {
+	     vector<RevEngEdge*> rev_edgs = sfs_reg[ki]->getAllRevEdges();
+	     if (rev_edgs.size() > 0)
+	       removed_edgs.insert(removed_edgs.end(), rev_edgs.begin(), rev_edgs.end());
+	     removed_surfs.push_back(sfs_reg[ki]->getSurface(0));
+	     sfs_reg[ki]->clearSurface();
+	   }
+       }
+    }
+  surfaceExtractOutput(-1, separate_groups, removed_surfs);
+  for (size_t ki=0; ki<removed_edgs.size(); ++ki)
+    {
+      size_t kj;
+      for (kj=0; kj<edges_.size(); ++kj)
+	if (edges_[kj].get() == removed_edgs[ki])
+	  break;
+      if (kj < edges_.size())
+	edges_.erase(edges_.begin()+kj);
+    }
+      
+  
+  int stop_break3 = 1;
+}
+
+//===========================================================================
+void RevEng::integrateInSmallSurfs(vector<shared_ptr<RevEngRegion> >& small_sf_reg,
+				   vector<RevEngRegion*>& nosf_reg,
+				   vector<RevEngRegion*>& include_reg)
+//===========================================================================
+{
+  // Sort small regions according to size (could possibly include also accuracy)
+  for (size_t ki=0; ki<small_sf_reg.size(); ++ki)
+    for (size_t kj=ki+1; kj<small_sf_reg.size(); ++kj)
+      if  (small_sf_reg[kj]->numPoints() > small_sf_reg[ki]->numPoints())
+	std::swap(small_sf_reg[ki], small_sf_reg[kj]);
+
+  // Check accuracy of not assigned regions with respect to the small surfaces
+  double angtol = 5.0*anglim_;
+  for (size_t ki=0; ki<nosf_reg.size(); ++ki)
+    {
+      BoundingBox bb1 = nosf_reg[ki]->getBbox();
+      vector<double> maxdist, avdist;
+      vector<int> num_in, num2_in;
+      vector<vector<double> > parvals;
+      vector<vector<pair<double,double> > > distang;
+      vector<int> sfflag;
+      vector<size_t> cand_ix;
+      for (size_t kj=0; kj<small_sf_reg.size(); ++kj)
+	{
+	  BoundingBox bb2 = small_sf_reg[kj]->getBbox();
+	  if (!bb1.overlaps(bb2, approx_tol_))
+	      continue;
+	  shared_ptr<ParamSurface> surf = small_sf_reg[kj]->getSurface(0)->surface();
+	  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+			  surf->instanceType() == Class_Cone);
+	  double maxd, avd;
+	  int inside, inside2;
+	  vector<double> param;
+	  vector<pair<double,double> > d_a;
+	  vector<RevEngPoint*> in, out;
+	  RevEngUtils::distToSurf(nosf_reg[ki]->pointsBegin(),
+				  nosf_reg[ki]->pointsEnd(), surf, approx_tol_,
+				  maxd, avd, inside, inside2, in, out,
+				  param, d_a, angtol);
+	  int sf_flag = small_sf_reg[kj]->defineSfFlag(0, approx_tol_, inside,
+						       inside2, avd, cyllike);
+	  if (sf_flag < ACCURACY_POOR)
+	    {
+	      // Check "closeness"
+	      double dom1[4];
+	      small_sf_reg[kj]->getDomain(dom1);
+	      RectDomain sf_dom(Vector2D(dom1[0],dom1[2]), Vector2D(dom1[1],dom1[3]));
+	      Vector2D c1(param[0], param[1]);
+	      Vector2D c2(param[0], param[1]);
+	      for (size_t kr=2; kr<param.size(); kr+=2)
+		{
+		  c1[0] = std::min(c1[0], param[kr]);
+		  c2[0] = std::max(c2[0], param[kr]);
+		  c1[1] = std::min(c1[1], param[kr+1]);
+		  c2[1] = std::max(c2[1], param[kr+1]);
+		}
+	      RectDomain pnt_dom(c1, c2);
+	      double udel = 0.25*(dom1[1]-dom1[0]);
+	      double vdel = 0.25*(dom1[3]-dom1[2]);
+	      BoundingBox bb1 = small_sf_reg[kj]->boundingBox();
+	      double ddel = 0.25*bb1.low().dist(bb1.high());
+	      BoundingBox bb2 = nosf_reg[ki]->boundingBox();
+	      if (sf_dom.overlap(pnt_dom, udel, vdel) &&
+		  bb1.overlaps(bb2, ddel))
+		{
+		  maxdist.push_back(maxd);
+		  avdist.push_back(avd);
+		  num_in.push_back(inside);
+		  num2_in.push_back(inside2);
+		  parvals.push_back(param);
+		  distang.push_back(d_a);
+		  sfflag.push_back(sf_flag);
+		  cand_ix.push_back(kj);
+		}
+	    }
+	}
+      if (cand_ix.size() > 0)
+	{
+	  // Sort candidates
+	  double eps = 0.01*approx_tol_;
+	  vector<int> perm(cand_ix.size());
+	  for (size_t kj=0; kj<perm.size(); ++kj)
+	    perm[kj] = kj;
+	  for (size_t kj=0; kj<perm.size(); ++kj)
+	    for (size_t kr=kj+1; kr<perm.size(); ++kr)
+	      if (sfflag[perm[kr]] < sfflag[perm[kj]] ||
+		  (sfflag[perm[kr]] == sfflag[perm[kj]] &&
+		   avdist[perm[kr]] < avdist[perm[kj]]-eps))
+		std::swap(perm[kj], perm[kr]);
+
+	  vector<RevEngRegion*> dummy;
+	  small_sf_reg[cand_ix[perm[0]]]->includeAdjacentRegion(nosf_reg[ki],
+								maxdist[perm[0]],
+								avdist[perm[0]],
+								num_in[perm[0]],
+								num2_in[perm[0]],
+								parvals[perm[0]],
+								distang[perm[0]],
+								dummy);
+	  include_reg.push_back(nosf_reg[ki]);
+	}
+      
+      int stop_break1 = 1;
+    }
+  int stop_break2 = 1;
+}
+
+bool checkAccuracy(RevEngRegion* reg, int min_num_pt, double tol, double angtol,
+		   shared_ptr<ElementarySurface> surf,
+		   vector<vector<RevEngPoint*> >& points, vector<BoundingBox>& bb,
+		   size_t& num_points, double& maxdist, double& avdist, int& num_in,
+		   int& num2_in, vector<vector<double> >& parvals,
+		   vector<vector<pair<double,double> > >& dist_ang,
+		   vector<RevEngPoint*>& non_assigned_pts)
+{
+  num_points = 0;
+  maxdist = 0.0;
+  avdist = 0.0;
+  num_in = 0;
+  num2_in = 0;
+  bool type_cyl = (surf->instanceType() == Class_Cylinder ||
+		   surf->instanceType() == Class_Cone);
+  for (size_t kj=0; kj<points.size(); )
+    {
+      if (points[kj].size() == 0)
+	{
+	  points.erase(points.begin()+kj);
+	  continue;
+	}
+      double maxd, avd;
+      int inside, inside2;
+      vector<double> param;
+      vector<pair<double,double> > d_a;
+      vector<RevEngPoint*> in, out;
+      RevEngUtils::distToSurf(points[kj].begin(), points[kj].end(),
+			      surf, tol, maxd, avd, inside,
+			      inside2, in, out, param, d_a, angtol);
+      int sf_flag = reg->defineSfFlag((int)points[kj].size(), 0,
+				      tol, inside, inside2, avd, type_cyl);
+      if (sf_flag >= ACCURACY_POOR)
+	{
+	  non_assigned_pts.insert(non_assigned_pts.end(), points[kj].begin(),
+				  points[kj].end());
+	  points.erase(points.begin()+kj);
+	  bb.erase(bb.begin()+kj);
+	}
+      else
+	{
+	  maxdist = std::max(maxdist, maxd);
+	  avdist += avd;
+	  num_in += inside;
+	  num2_in += inside2;
+	  parvals.push_back(param);
+	  dist_ang.push_back(d_a);
+	  num_points += points[kj].size();
+	  ++kj;
+	}
+    }
+  avdist /= (double)parvals.size();
+  if ((int)num_points < min_num_pt)
+    {
+      for (size_t kj=0; kj<points.size(); ++kj)
+	if (points[kj].size() > 0)
+	  non_assigned_pts.insert(non_assigned_pts.end(), points[kj].begin(),
+				  points[kj].end());
+      return false;
+    }
+  return true;
+}
+
+//===========================================================================
+void RevEng::extractSmallSurfs(SmallSurface& small_surf,
+			       vector<shared_ptr<RevEngRegion> >& small_sf_reg,
+			       vector<shared_ptr<HedgeSurface> >& small_sf_hedge,
+			       vector<RevEngRegion*>& nosf_reg,
+			       vector<RevEngPoint*>& non_assigned_pts)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  double angtol = 5.0*anglim_;
+  int min_num_pt = min_point_region_/10;
+  int classtype = CLASSIFICATION_UNDEF;
+  
+  vector<shared_ptr<ElementarySurface> > surfs = small_surf.surfs_;
+  vector<vector<vector<RevEngPoint*> > > points(surfs.size());
+  vector<vector<BoundingBox> > bb(surfs.size());
+  if (surfs.size() == 1)
+    {
+      points[0] = small_surf.assos_points_;
+      bb[0] = small_surf.bbox_;
+    }
+  else
+    {
+      // Distribute points according to surfaces
+      for (size_t ki=0; ki<surfs.size(); ++ki)
+	{
+	  points[ki].resize(small_surf.assos_points_.size());
+	  bb[ki].resize(small_surf.assos_points_.size());
+	  for (size_t kr=0; kr<bb[ki].size(); ++kr)
+	    bb[ki][kr] = BoundingBox(3);
+	}
+
+      for (size_t ki=0; ki<small_surf.assos_points_.size(); ++ki)
+	{
+	  for (size_t kj=0; kj<small_surf.assos_points_[ki].size(); ++kj)
+	    {
+	      Vector3D xyz = small_surf.assos_points_[ki][kj]->getPoint();
+	      Point pos(xyz[0], xyz[1], xyz[2]);
+
+	      double mind = std::numeric_limits<double>::max();
+	      int min_ix = -1;
+	      for (size_t kr=0; kr<surfs.size(); ++kr)
+		{
+		  double upar, vpar, dist;
+		  Point close;
+		  surfs[kr]->closestPoint(pos, upar, vpar, close, dist, eps);
+		  if (dist < mind)
+		    {
+		      mind = dist;
+		      min_ix = (int)kr;
+		    }
+		}
+	      if (min_ix >= 0)
+		{
+		  points[min_ix][ki].push_back(small_surf.assos_points_[ki][kj]);
+		  bb[min_ix][ki].addUnionWith(pos);
+		}
+	    }
+	}
+    }
+
+  for (size_t ki=0; ki<surfs.size(); ++ki)
+    {
+      size_t npt = 0;
+      for (size_t kj=0; kj<points[ki].size(); ++kj)
+	npt += points[ki][kj].size();
+      if ((int)npt < min_num_pt)
+	{
+	  for (size_t kj=0; kj<points[ki].size(); ++kj)
+	    if (points[ki][kj].size() > 0)
+	      non_assigned_pts.insert(non_assigned_pts.end(), points[ki][kj].begin(),
+				      points[ki][kj].end());
+	  continue;
+	}
+      
+      shared_ptr<ElementarySurface> curr_sf;
+      bool type_cyl = (surfs[ki]->instanceType() == Class_Cylinder ||
+		       surfs[ki]->instanceType() == Class_Cone);
+      if (points[ki].size() > 1)
+	{
+	  // Recompute free surface parameters
+	  vector<RevEngPoint*> all_points;
+	  for (size_t kj=0; kj<points[ki].size(); ++kj)
+	    all_points.insert(all_points.end(), points[ki][kj].begin(),
+			      points[ki][kj].end());
+	  double diag = bbox_.low().dist(bbox_.high());
+	  curr_sf = RevEngUtils::elemsurfWithAxis(surfs[ki], all_points, mainaxis_, diag);
+	}
+      else
+	curr_sf = surfs[ki];
+
+      // Check accuracy and extract point groups with a large distance.
+      size_t num_points = 0;
+      double maxdist = 0.0, avdist = 0.0;
+      int num_in = 0, num2_in = 0;
+      vector<vector<double> > parvals;
+      vector<vector<pair<double,double> > > dist_ang;
+      bool OK = checkAccuracy(nosf_reg[0], min_num_pt, approx_tol_, angtol,
+			      curr_sf, points[ki], bb[ki],
+			      num_points, maxdist, avdist, num_in, num2_in,
+			      parvals, dist_ang, non_assigned_pts);
+		    
+      if (!OK)
+	continue;
+
+#ifdef DEBUG_SMALL
+      std::ofstream of1("cand_sf_points.g2");
+      curr_sf->writeStandardHeader(of1);
+      curr_sf->write(of1);
+      for (size_t kv=0; kv<points[ki].size(); ++kv)
+	{
+	  if (points[ki][kv].size() > 0)
+	    {
+	      of1 << "400 1 0 0" << std::endl;
+	      of1 << points[ki][kv].size() << std::endl;
+	      for (size_t kw=0; kw<points[ki][kv].size(); ++kw)
+		of1 << points[ki][kv][kw]->getPoint() << std::endl;
+	    }
+	}
+#endif
+      double distlim = 2.0*approx_tol_;
+      if (maxdist > distlim)
+	{
+	  // Remove the most distant points and try again
+	  int num2 = 0;
+	  for (size_t kj=0; kj<points[ki].size(); ++kj)
+	    {
+	      bb[ki][kj] = BoundingBox(3);
+	      for (size_t kr=0; kr<points[ki][kj].size(); )
+		{
+		  if (dist_ang[kj][kr].first > distlim)
+		    points[ki][kj].erase(points[ki][kj].begin()+kr);
+		  else
+		    {
+		      Vector3D xyz = points[ki][kj][kr]->getPoint();
+		      Point pos(xyz[0], xyz[1], xyz[2]);
+		      bb[ki][kj].addUnionWith(pos);
+		      num2++;
+		      ++kr;
+		    }
+		}
+	    }
+	  if (num2 < min_num_pt)
+	    continue;
+
+	  // Recompute free surface parameters
+	  vector<RevEngPoint*> all_points;
+	  for (size_t kj=0; kj<points[ki].size(); ++kj)
+	    all_points.insert(all_points.end(), points[ki][kj].begin(),
+			      points[ki][kj].end());
+	  double diag = bbox_.low().dist(bbox_.high());
+	  shared_ptr<ElementarySurface> curr_sf2;
+	  curr_sf2 = RevEngUtils::elemsurfWithAxis(curr_sf, all_points, mainaxis_, diag);
+	  std::swap(curr_sf, curr_sf2);
+
+	  OK = checkAccuracy(nosf_reg[0], min_num_pt, approx_tol_, angtol,
+			     curr_sf, points[ki], bb[ki],
+			     num_points, maxdist, avdist, num_in, num2_in,
+			     parvals, dist_ang, non_assigned_pts);
+		    
+	  if (!OK)
+	    continue;
+	  
+#ifdef DEBUG_SMALL
+	  std::ofstream of3("cand_sf_points2.g2");
+	  curr_sf->writeStandardHeader(of3);
+	  curr_sf->write(of3);
+	  for (size_t kv=0; kv<points[ki].size(); ++kv)
+	    {
+	      if (points[ki][kv].size() > 0)
+		{
+		  of3 << "400 1 0 0" << std::endl;
+		  of3 << points[ki][kv].size() << std::endl;
+		  for (size_t kw=0; kw<points[ki][kv].size(); ++kw)
+		    of3 << points[ki][kv][kw]->getPoint() << std::endl;
+		}
+	    }
+#endif
+	}
+      
+      // Compute parameter domains
+      vector<RectDomain> dom(parvals.size());
+      for (size_t kj=0; kj<parvals.size(); ++kj)
+	{
+	  Vector2D c1(parvals[kj][0], parvals[kj][1]);
+	  Vector2D c2(parvals[kj][0], parvals[kj][1]);
+	  for (size_t kr=2; kr<parvals[kj].size(); kr+=2)
+	    {
+	      c1[0] = std::min(c1[0], parvals[kj][kr]);
+	      c2[0] = std::max(c2[0], parvals[kj][kr]);
+	      c1[1] = std::min(c1[1], parvals[kj][kr+1]);
+	      c2[1] = std::max(c2[1], parvals[kj][kr+1]);
+	    }
+	  dom[kj] = RectDomain(c1, c2);
+	}
+
+      // Sort groups according to size
+      RectDomain totdom = dom[0];
+      vector<double> urange(dom.size());
+      vector<double> vrange(dom.size());
+      vector<double> diag(dom.size());
+      vector<size_t> perm(points[ki].size());
+      for (size_t kj=0; kj<perm.size(); ++kj)
+	{
+	  perm[kj] = kj;
+	  totdom.addUnionWith(dom[kj]);
+	  urange[kj] = dom[kj].umax() - dom[kj].umin();
+	  vrange[kj] = dom[kj].vmax() - dom[kj].vmin();
+	  diag[kj] = bb[ki][kj].low().dist(bb[ki][kj].high());
+	}
+      for (size_t kj=0; kj<perm.size(); ++kj)
+	for (size_t kr=kj+1; kr<perm.size(); ++kr)
+	  if (points[ki][perm[kr]].size() > points[ki][perm[kj]].size())
+	    std::swap(perm[kr], perm[kj]);
+
+      vector<size_t> first_group;
+      size_t ix = 0;
+      first_group.push_back(ix);
+      size_t kj=0, kr=0, kh=0, kv=0;
+      for (kj=0; kj<perm.size(); kj=kr)
+	{
+	  for (kr=kj+1, kv=kj+2; kr<perm.size(); )
+	    {
+	      for (kh=ix; kh<kr; ++kh)
+		{
+		  double udel = 0.125*(urange[perm[kr]]+urange[perm[kh]]);
+		  double vdel = 0.125*(vrange[perm[kr]]+vrange[perm[kh]]);
+		  double ddel = 0.125*(diag[perm[kr]]+diag[perm[kh]]);
+		  if (dom[perm[kh]].overlap(dom[perm[kr]], udel, vdel) &&
+		      bb[ki][perm[kh]].overlaps(bb[ki][perm[kr]], ddel))
+		    break;
+		}
+	      if (kh >= kr)
+		{
+		  // Not close
+		  if (kv >= perm.size())
+		    {
+		      ix = kr;
+		      first_group.push_back(ix);
+		      break;
+		    }
+		  if (kr < perm.size()-1)
+		    std::swap(perm[kr], perm[kv]);
+		  ++kv;
+		}
+	      else
+		{
+		  ++kr;
+		  kv = kr+1;
+		}
+	    }
+	}
+      first_group.push_back(perm.size());
+      
+#ifdef DEBUG_SMALL
+      std::ofstream of2("cand_sf_points4.g2");
+      curr_sf->writeStandardHeader(of2);
+      curr_sf->write(of2);
+      for (size_t kj=1; kj<first_group.size(); ++kj)
+	{
+	  size_t num = 0;
+	  for (size_t kv=first_group[kj-1]; kv<first_group[kj]; ++kv)
+	    num += points[ki][perm[kv]].size();
+	  of2 << "400 1 0 0" << std::endl;
+	  of2 << num << std::endl;
+	  for (size_t kv=first_group[kj-1]; kv<first_group[kj]; ++kv)
+	    for (size_t kw=0; kw<points[ki][perm[kv]].size(); ++kw)
+	      of2 << points[ki][perm[kv]][kw]->getPoint() << std::endl;
+	}
+#endif
+       for (size_t kj=1; kj<first_group.size(); ++kj)
+	{
+	  size_t num = 0;
+	  for (size_t kv=first_group[kj-1]; kv<first_group[kj]; ++kv)
+	    num += points[ki][perm[kv]].size();
+
+	  if (num < min_num_pt)
+	    {
+	      for (size_t kv=first_group[kj-1]; kv<first_group[kj]; ++kv)
+		if (points[ki][perm[kv]].size() > 0)
+	      non_assigned_pts.insert(non_assigned_pts.end(), 
+				      points[ki][perm[kv]].begin(),
+				      points[ki][perm[kv]].end());
+	    }
+	  else
+	    {
+	      // Create region with associated surface
+	      // First remove the associated points from their previous region
+	      // Collect points with respect to regions
+	      vector<vector<RevEngPoint*> > reg_points;
+	      for (size_t kv=first_group[kj-1]; kv<first_group[kj]; ++kv)
+		for (size_t kw=0; kw<points[ki][perm[kv]].size(); ++kw)
+		  {
+		    RevEngPoint *currpt = points[ki][perm[kv]][kw];
+		    RevEngRegion *reg = currpt->region();
+		    size_t kr;
+		    for (kr=0; kr<reg_points.size(); ++kr)
+		      if (reg_points[kr][0]->region() == reg)
+			break;
+		    if (kr < reg_points.size())
+		      reg_points[kr].push_back(currpt);
+		    else
+		      {
+			vector<RevEngPoint*> curr_reg_pt;
+			curr_reg_pt.push_back(currpt);
+			reg_points.push_back(curr_reg_pt);
+		      }
+		  }
+
+	      for (size_t kr=0; kr<reg_points.size(); ++kr)
+		{
+		  RevEngRegion *reg = reg_points[kr][0]->region();
+		  reg->removePoints(reg_points[kr]);
+		}
+
+	      // Collect points and set parameter value
+	      vector<RevEngPoint*> surf_pts;
+	      for (size_t kv=first_group[kj-1]; kv<first_group[kj]; ++kv)
+		for (size_t kw=0; kw<points[ki][perm[kv]].size(); ++kw)
+		  {
+		    RevEngPoint *currpt = points[ki][perm[kv]][kw];
+		    double u1 = parvals[perm[kv]][2*kw];
+		    double v1 = parvals[perm[kv]][2*kw+1];
+		    currpt->setPar(Vector2D(u1,v1));
+		    double dd = dist_ang[perm[kv]][kw].first;
+		    double ang = dist_ang[perm[kv]][kw].second;
+		    currpt->setSurfaceDist(dd, ang);
+		    surf_pts.push_back(currpt);
+		  }
+
+	      shared_ptr<RevEngRegion> small_reg(new RevEngRegion(classtype,
+								  edge_class_type_,
+								  surf_pts));
+	      shared_ptr<HedgeSurface> hedge(new HedgeSurface(curr_sf, small_reg.get()));
+	      small_reg->setHedge(hedge.get());
+	      small_reg->updateInfo(approx_tol_, angtol);
+	      int sf_flag = small_reg->defineSfFlag(0, approx_tol_, num_in, num2_in,
+						   avdist, type_cyl);
+	      small_reg->setSurfaceFlag(sf_flag);
+	      small_sf_reg.push_back(small_reg);
+	      small_sf_hedge.push_back(hedge);
+
+	      
+	      int stop_remove = 1;
+	    }
+	}
+     int stop_break = 1;
+    }
+}
+
+
+
+//===========================================================================
+bool RevEng::identifySmallRotational(vector<RevEngPoint*>& points,
+				     Point midp, Point loc0, Point axis, Point Cx,
+				     double ppar1, double ppar2,
+				     vector<shared_ptr<ElementarySurface> >& sfs)
+//===========================================================================
+{
+  double eps = 1.0e-9;
+  bool found = false;
+  double num_fac = 0.5;
+  double anglim2 = 5*anglim_; //10*anglim_;
+  int num_pt_lim = min_point_region_/20;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group_points;
+  group_points.push_back(std::make_pair(points.begin(), points.end()));
+
+  if (loc0.dimension() == 0)
+    {
+      // Define point from cylinder algorithm
+      Point low = bbox_.low();
+      Point high = bbox_.high();
+      shared_ptr<Cylinder> tmp_cyl =
+	RevEngUtils::cylinderWithAxis(points, axis, low, high, mainaxis_);
+      loc0 = tmp_cyl->location();
+    }
+  Point loc = loc0 + ((midp - loc0)*axis)*axis;
+
+  vector<Point> rotated;
+  RevEngUtils::rotateToPlane(group_points, Cx, axis, loc, rotated);
+  Point loc1 = loc + ppar1*axis;
+  Point loc2  = loc + ppar2*axis;
+#ifdef DEBUG_SMALL
+  std::ofstream of4("axis_rotate.g2");
+  of4 << "400 1 0 4 255 0 0 255" << std::endl;
+  of4 << rotated.size() << std::endl;
+  for (size_t kr=0; kr<rotated.size(); ++kr)
+    of4 << rotated[kr] << std::endl;
+
+  of4 << "410 1 0 0" << std::endl;
+  of4 << "1" << std::endl;
+  of4 << loc1 << " " << loc2 << std::endl;
+#endif
+
+  // Parameterize rotated points on axis
+  shared_ptr<SplineCurve> line_cv(new SplineCurve(loc1, loc2));
+  vector<double> pts;
+  vector<double> param;
+  vector<double> distance;
+  double tmin = line_cv->startparam();
+  double tmax = line_cv->endparam();
+  double tmin2 = tmax;
+  double tmax2 = tmin;
+  for (size_t ki=0; ki<rotated.size(); ++ki)
+    {
+      double tpar, dist;
+      Point close;
+      line_cv->closestPoint(rotated[ki], tmin, tmax, tpar, close, dist);
+      pts.insert(pts.end(), rotated[ki].begin(), rotated[ki].end());
+      param.push_back(tpar);
+      distance.push_back(dist);
+      tmin2 = std::min(tmin2, tpar);
+      tmax2 = std::max(tmax2, tpar);
+    }
+
+  if (tmax2 - tmin2 < eps)
+    return false;
+
+  // Statistics
+  int num_del = 10;
+  double tdel = (tmax2 - tmin2)/(double)(num_del);
+  vector<int> num_pts(num_del, 0);
+  vector<double> min_dist(num_del, std::numeric_limits<double>::max());
+  vector<double> max_dist(num_del, std::numeric_limits<double>::lowest());
+  vector<double> avdist(num_del, 0.0);
+  for (size_t kj=0; kj<param.size(); ++kj)
+    {
+      int ka = (int)((param[kj]-tmin2)/tdel);
+      if (ka >= (int)num_pts.size())
+	ka = (int)num_pts.size() - 1;
+      num_pts[ka]++;
+      min_dist[ka] = std::min(min_dist[ka], distance[kj]);
+      max_dist[ka] = std::max(max_dist[ka], distance[kj]);
+      avdist[ka] += distance[kj];
+    }
+
+  vector<double> range(num_del);
+  vector<double> var(num_del);
+  vector<double> bddist(num_del);
+  vector<double> rfrac(num_del);
+  double avnum = 0.0, avrange = 0.0, avbdd = 0.0, avrfrac = 0.0;
+  double fac = 1.0/(double)num_del;
+  for (size_t kj=0; kj<avdist.size(); ++kj)
+    {
+      if (num_pts[kj] > 0)
+	avdist[kj] /= (double)num_pts[kj];
+      range[kj] = max_dist[kj] - min_dist[kj];
+      var[kj] = 0.5*(min_dist[kj]+max_dist[kj]) - avdist[kj];
+      bddist[kj] = std::min(avdist[kj]-min_dist[kj], max_dist[kj]-avdist[kj]);
+      rfrac[kj] = range[kj]/(double)num_pts[kj];
+      avnum += fac*(double)num_pts[kj];
+      avrange += fac*range[kj];
+      avbdd += fac*bddist[kj];
+      avrfrac += fac*rfrac[kj];
+    }
+  double bdfrac = avbdd/avrange;
+
+  double numfac = 1.5;
+  double rfac = 1.5;
+  double rffac = 5.0;  // Need another criterion
+
+  // Shorten curve
+  double tmin3 = tmin2, tmax3 = tmax2;
+  for (int ka=0; ka<num_del; ++ka)
+    {
+      if (((double)num_pts[ka] > numfac*avnum || rfrac[ka] > rffac*avrfrac) &&
+	  range[ka] > rfac*avrange && bddist[ka]/range[ka] > bdfrac)
+	tmin3 += tdel;
+      else
+	break;
+    }
+  for (int ka=num_del-1; ka>=0; --ka)
+    {
+      if (((double)num_pts[ka] > numfac*avnum || rfrac[ka] > rffac*avrfrac) &&
+	  range[ka] > rfac*avrange && bddist[ka]/range[ka] > bdfrac)
+	tmax3 -= tdel;
+      else
+	break;
+    }
+
+  if (tmin3 >= tmax3)
+    return found;
+
+  vector<Point> rotated2;
+  vector<double> param2;
+  if (tmin3 > tmin2 || tmax3 < tmax2)
+    {
+      for (size_t ki=0; ki<param.size(); ++ki)
+	if (param[ki] >= tmin3 && param[ki] <= tmax3)
+	  {
+	    rotated2.push_back(rotated[ki]);
+	    param2.push_back(param[ki]);
+	  }
+    }
+  else
+    {
+      rotated2 = rotated;
+      param2 = param;
+    }
+
+  int in = 12;
+  int ik = 3;
+  shared_ptr<SplineCurve> approx_cv;
+  RevEngUtils::curveApprox(rotated2, param2, ik, in, approx_cv);
+  
+#ifdef DEBUG_SMALL
+  approx_cv->writeStandardHeader(of4);
+  approx_cv->write(of4);
+#endif
+
+  shared_ptr<SplineCurve> approx_line0;
+  RevEngUtils::curveApprox(rotated2, param2, 2, 2, approx_line0);
+  
+#ifdef DEBUG_SMALL
+  approx_line0->writeStandardHeader(of4);
+  approx_line0->write(of4);
+#endif
+
+  // Check accuracy of line
+  vector<Point> der0(2);
+  approx_line0->point(der0,
+		     0.5*(approx_line0->startparam()+approx_line0->endparam()), 1);
+  shared_ptr<Line> curr_line0(new Line(der0[0], der0[1]));
+
+  double maxdist0, avdist0;
+  int num_in0;
+  vector<double> curr_dist0;
+  RevEngUtils::distToCurve(rotated, curr_line0, approx_tol_, maxdist0,
+			   avdist0, num_in0, curr_dist0);
+  if (num_in0 > (int)rotated.size()/2 && avdist0 <= approx_tol_)
+    {
+      // Define surface
+      double ang = der0[1].angle(axis);
+      ang = std::min(ang, M_PI-ang);
+      Point axis_pt = loc + ((der0[0]-loc)*axis)*axis;
+      double rad = der0[0].dist(axis_pt);
+      if (ang < anglim2)
+	{
+	  // Create cylinder
+	  shared_ptr<Cylinder> cyl(new Cylinder(rad, axis_pt, axis, Cx));
+	  sfs.push_back(cyl);
+	}
+      else
+	{
+	  // Create cone
+	  // Sign of angle
+	  Point pos3;
+	  approx_line0->point(pos3, approx_line0->endparam());
+	  Point axis_pt2 = loc + ((pos3-loc)*axis)*axis;
+	  double rad2 = pos3.dist(axis_pt2);
+	  int sgn = ((pos3 - der0[0])*axis < 0.0) ? -1 : 1;
+	  if (sgn*rad2 < sgn*rad)
+	    ang *= -1;
+	  
+	  shared_ptr<Cone> cone(new Cone(rad, axis_pt, axis, Cx, ang));
+	  sfs.push_back(cone);
+	}
+      return true;
+    }
+
+  // Angle between consecutive control segments
+  vector<double> seg_ang(in-2);
+  vector<double> coefs(approx_cv->coefs_begin(), approx_cv->coefs_end());
+  int dim = 3;
+  Point pt1(coefs.begin(), coefs.begin()+dim);
+  Point pt2(coefs.begin()+dim, coefs.begin()+2*dim);
+  for (int ka=2; ka<in; ++ka)
+    {
+      Point pt3(coefs.begin()+ka*dim,coefs.begin()+(ka+1)*dim);
+      Point vec1 = pt2 - pt1;
+      Point vec2 = pt3 - pt2;
+      seg_ang[ka-2] = vec1.angle(vec2);
+      pt1 = pt2;
+      pt2 = pt3;
+    }
+
+  // Potential lines
+  vector<vector<Point> > line_coefs;
+  vector<vector<double> > line_par;
+  vector<Point> currc;
+  vector<double> currp;
+  for (int ka=0; ka<(int)seg_ang.size(); ++ka)
+    {
+      if (seg_ang[ka] > anglim2)
+	{
+	  if (currc.size() > 0)
+	    {
+	      line_coefs.push_back(currc);
+	      line_par.push_back(currp);
+	      currc.clear();
+	      currp.clear();
+	    }
+	}
+      else
+	{
+	  if (currc.size() == 0)
+	    {
+	      currc.push_back(Point(coefs.begin()+ka*dim,coefs.begin()+(ka+1)*dim));
+	      currc.push_back(Point(coefs.begin()+(ka+1)*dim,coefs.begin()+(ka+2)*dim));
+	      currp.push_back(approx_cv->basis().grevilleParameter(ka));
+	      currp.push_back(approx_cv->basis().grevilleParameter(ka+1));
+	    }
+	  currc.push_back(Point(coefs.begin()+(ka+2)*dim,coefs.begin()+(ka+3)*dim));
+	  currp.push_back(approx_cv->basis().grevilleParameter(ka+2));
+
+	}
+    }
+
+  if (line_par.size() == 0)
+    {
+      if (currp.size() < 2 || currp[currp.size()-1] - currp[0] < eps)
+	return false;
+      vector<double> tmp_par(2);
+      tmp_par[0] = currp[0];
+      tmp_par[1] = currp[currp.size()-1];
+      line_par.push_back(tmp_par);
+    }
+  
+  for (size_t kj=0; kj<line_par.size(); ++kj)
+    {
+      double tmin4 = line_par[kj][0];
+      double tmax4 = line_par[kj][line_par[kj].size()-1];
+      vector<Point> rotated3;
+      vector<double> param3;
+      if (tmin4 > tmin3 || tmax4 < tmax3)
+	{
+	  for (size_t kr=0; kr<param2.size(); ++kr)
+	    if (param2[kr] >= tmin4 && param2[kr] <= tmax4)
+	      {
+		rotated3.push_back(rotated2[kr]);
+		param3.push_back(param2[kr]);
+	      }
+	}
+      else
+	{
+	  rotated3 = rotated2;
+	  param3 = param2;
+	}
+
+      if ((int)rotated3.size() < num_pt_lim)
+	continue;
+      
+      shared_ptr<SplineCurve> approx_line;
+      RevEngUtils::curveApprox(rotated3, param3, 2, 2, approx_line);
+      
+#ifdef DEBUG_SMALL
+      approx_line->writeStandardHeader(of4);
+      approx_line->write(of4);
+#endif
+
+      // Check accuracy for all points
+      vector<Point> der(2);
+      approx_line->point(der,
+			 0.5*(approx_line->startparam()+approx_line->endparam()), 1);
+      shared_ptr<Line> curr_line(new Line(der[0], der[1]));
+
+      double maxdist, avdist;
+      int num_in;
+      vector<double> curr_dist;
+      RevEngUtils::distToCurve(rotated, curr_line, approx_tol_, maxdist,
+			       avdist, num_in, curr_dist);
+
+      // double rad = 0.0;
+      // for (size_t kr=0; kr<curr_dist.size(); ++kr)
+      // 	{
+      // 	  if (curr_dist[kr] <= approx_tol_)
+      // 	    {
+      // 	      Point tmp = loc + ((rotated[kr]-loc)*axis)*axis;
+      // 	      double rad0 = rotated[kr].dist(tmp);
+      // 	      rad += rad0;
+      // 	    }
+      // 	}
+      // rad /= (double)num_in;
+	  
+#ifdef DEBUG_SMALL
+      std::ofstream of1("in_out_curr.g2");
+      vector<Point> in_pt, out_pt;
+      for (size_t kr=0; kr<curr_dist.size(); ++kr)
+	{
+	  if (curr_dist[kr] <= approx_tol_)
+	    in_pt.push_back(rotated[kr]);
+	  else
+	    out_pt.push_back(rotated[kr]);
+	}
+      if (in_pt.size() > 0)
+	{
+	  of1 << "400 1 0 4 255 0 0 255" << std::endl;
+	  of1 << in_pt.size() << std::endl;
+	  for (size_t kr=0; kr<in_pt.size(); ++kr)
+	    of1 << in_pt[kr] << std::endl;
+	}
+      if (out_pt.size() > 0)
+	{
+	  of1 << "400 1 0 4 0 255 0 255" << std::endl;
+	  of1 << out_pt.size() << std::endl;
+	  for (size_t kr=0; kr<out_pt.size(); ++kr)
+	    of1 << out_pt[kr] << std::endl;
+	}
+#endif
+
+      // Remove most distant points and try again
+      double dlim = 2.0*approx_tol_;
+      vector<Point> rotated4;
+      vector<double> param4;
+      for (size_t kr=0; kr<curr_dist.size(); ++kr)
+	{
+	  if (curr_dist[kr] <= dlim)
+	    {
+	      rotated4.push_back(rotated[kr]);
+	      param4.push_back(param[kr]);
+	    }
+	}
+
+      shared_ptr<SplineCurve> approx_line2;
+      RevEngUtils::curveApprox(rotated4, param4, 2, 2, approx_line2);
+      
+#ifdef DEBUG_SMALL
+      std::ofstream of2("in_out2_curr.g2");
+      approx_line2->writeStandardHeader(of2);
+      approx_line2->write(of2);
+#endif
+
+      // Check accuracy for all points
+      vector<Point> der2(2);
+      approx_line2->point(der2,
+			 0.5*(approx_line2->startparam()+approx_line2->endparam()), 1);
+      shared_ptr<Line> curr_line2(new Line(der2[0], der2[1]));
+
+      double maxdist2, avdist2;
+      int num_in2;
+      vector<double> curr_dist2;
+      RevEngUtils::distToCurve(rotated, curr_line2, approx_tol_, maxdist2,
+			       avdist2, num_in2, curr_dist2);
+     // double rad2 = 0.0;
+     //  for (size_t kr=0; kr<curr_dist.size(); ++kr)
+     // 	{
+     // 	  if (curr_dist2[kr] <= approx_tol_)
+     // 	    {
+     // 	      Point tmp = loc + ((rotated[kr]-loc)*axis)*axis;
+     // 	      double rad0 = rotated[kr].dist(tmp);
+     // 	      rad2 += rad0;
+     // 	    }
+     // 	}
+     //  rad2 /= (double)num_in2;
+      
+#ifdef DEBUG_SMALL
+      vector<Point> in_pt2, out_pt2;
+      for (size_t kr=0; kr<curr_dist2.size(); ++kr)
+	{
+	  if (curr_dist2[kr] <= approx_tol_)
+	    in_pt2.push_back(rotated[kr]);
+	  else
+	    out_pt2.push_back(rotated[kr]);
+	}
+      if (in_pt.size() > 0)
+	{
+	  of2 << "400 1 0 4 255 0 0 255" << std::endl;
+	  of2 << in_pt2.size() << std::endl;
+	  for (size_t kr=0; kr<in_pt2.size(); ++kr)
+	    of2 << in_pt2[kr] << std::endl;
+	}
+      if (out_pt2.size() > 0)
+	{
+	  of2 << "400 1 0 4 0 255 0 255" << std::endl;
+	  of2 << out_pt2.size() << std::endl;
+	  for (size_t kr=0; kr<out_pt2.size(); ++kr)
+	    of2 << out_pt2[kr] << std::endl;
+	}
+#endif
+
+      // Define surface
+      if ((double)(std::max(num_in0, std::max(num_in, num_in2))) <
+	  num_fac*(double)rotated2.size())
+	continue;
+      if (num_in0 > std::max(num_in, num_in2))
+	{
+	  //std::swap(rad, rad2);
+	  std::swap(der0[0], der2[0]);
+	  std::swap(der0[1], der2[1]);
+	}
+      else if (num_in > num_in2)
+	{
+	  //std::swap(rad, rad2);
+	  std::swap(der[0], der2[0]);
+	  std::swap(der[1], der2[1]);
+	}
+      if (std::max(num_in0, std::max(num_in, num_in2)) > num_pt_lim)
+	{
+	  double ang = der2[1].angle(axis);
+	  ang = std::min(ang, M_PI-ang);
+	  Point axis_pt = loc + ((der2[0]-loc)*axis)*axis;
+	  double rad = der2[0].dist(axis_pt);
+	  if (ang < anglim2)
+	    {
+	      // Create cylinder
+	      shared_ptr<Cylinder> cyl(new Cylinder(rad, axis_pt, axis, Cx));
+	      sfs.push_back(cyl);
+	    }
+	  else
+	    {
+	      // Create cone
+	      // Sign of angle
+	      Point pos3;
+	      if (num_in2 > num_in) 
+		approx_line2->point(pos3, approx_line2->endparam());
+	      else
+		approx_line->point(pos3, approx_line->endparam());
+	      Point axis_pt2 = loc + ((pos3-loc)*axis)*axis;
+	      double rad2 = pos3.dist(axis_pt2);
+	      int sgn = ((pos3 - der[0])*axis < 0.0) ? -1 : 1;
+	      if (sgn*rad2 < sgn*rad)
+		ang *= -1;
+	      
+	      shared_ptr<Cone> cone(new Cone(rad, axis_pt, axis, Cx, ang));
+	      sfs.push_back(cone);
+	    }
+	  found = true;
+	}
+    }
+
+  return found;
+}
+
+//===========================================================================
+bool RevEng::identifySmallPlanar(vector<RevEngPoint*>& points,
+				 Point loc, Point axis, Point Cx,
+				 double ppar1, double ppar2, double delta,
+				 vector<shared_ptr<ElementarySurface> >& sfs)
+//===========================================================================
+{
+  // Sort groups with respect to axis
+  vector<Point> axis_pt(points.size());
+  vector<double> axis_par(points.size());
+  double tmin = std::numeric_limits<double>::max();
+  double tmax = std::numeric_limits<double>::lowest();
+  double avpar = 0.0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pos(xyz[0], xyz[1], xyz[2]);
+      double tpar = (pos - loc)*axis;
+      tmin = std::min(tmin, tpar);
+      tmax = std::max(tmax, tpar);
+      axis_par[ki] = tpar;
+      axis_pt[ki] = loc + axis_par[ki]*axis;
+    }
+
+  size_t ki;
+  double tlim = 1.5*approx_tol_;
+  int nlim = 100;  // Currently
+  double angtol = 5.0*anglim_;
+  bool found = false;
+  int num = 0;
+  Point mid(0.0, 0.0, 0.0);
+  for (ki=0; ki<axis_par.size(); ++ki)
+    {
+      if (axis_par[ki] <= ppar1+delta || axis_par[ki] >= ppar2-delta)
+	continue;
+
+      Point vec = axis_pt[ki] - loc;
+      Point pos2 = loc + (vec*axis)*axis;
+      mid += pos2;
+      ++num;
+    }
+
+  if (num == 0)
+    return false;
+  mid /= (double)num;
+      
+  shared_ptr<Plane> plane(new Plane(mid, axis, Cx));
+#ifdef DEBUG_SMALL
+  std::ofstream of("small_plane.g2");
+  plane->writeStandardHeader(of);
+  plane->write(of);
+#endif
+
+  // Check accuracy
+  double maxd, avd;
+  int n_in, n2_in;
+  vector<double> parvals;
+  vector<pair<double,double> > distang;
+  vector<RevEngPoint*> in, out;
+  RevEngUtils::distToSurf(points, plane, approx_tol_, angtol,
+			  maxd, avd, n_in, n2_in, distang);
+
+  if (avd <= approx_tol_ && n2_in > points.size()/2)
+    {
+      sfs.push_back(plane);
+      found = true;
+    }
+  return found;
+}
+ 
+//===========================================================================
+bool RevEng::identifySmallPlanar(vector<RevEngRegion*>& groups,
+				 Point loc, Point axis, Point Cx,
+				 double ppar1, double ppar2, double delta,
+				 vector<shared_ptr<ElementarySurface> >& sfs)
+//===========================================================================
+{
+  // Sort groups with respect to axis
+  vector<Point> axis_pt(groups.size());
+  vector<double> axis_par(groups.size());
+  vector<pair<double,double> > axis_range(groups.size());
+  for (size_t ki=0; ki<groups.size(); ++ki)
+    {
+      int num = groups[ki]->numPoints();
+      double tmin = std::numeric_limits<double>::max();
+      double tmax = std::numeric_limits<double>::lowest();
+      double avpar = 0.0;
+      double fac = 1.0/(double)num;
+      for (int ka=0; ka<num; ++ka)
+	{
+	  Vector3D xyz = groups[ki]->getPoint(ka)->getPoint();
+	  Point pos(xyz[0], xyz[1], xyz[2]);
+	  double tpar = (pos - loc)*axis;
+	  tmin = std::min(tmin, tpar);
+	  tmax = std::max(tmax, tpar);
+	  avpar += fac*tpar;
+	}
+      axis_par[ki] = avpar;
+      axis_range[ki] = std::make_pair(tmin,tmax);
+      axis_pt[ki] = loc + axis_par[ki]*axis;
+    }
+
+  for (size_t ki=0; ki<groups.size(); ++ki)
+    for (size_t kj=ki+1; kj<groups.size(); ++kj)
+      {
+	if (axis_par[kj] < axis_par[ki])
+	  {
+	    std::swap(axis_par[ki], axis_par[kj]);
+	    std::swap(axis_pt[ki], axis_pt[kj]);
+	    std::swap(axis_range[ki], axis_range[kj]);
+	    std::swap(groups[ki], groups[kj]);
+	  }
+      }
+
+  size_t ki, kj;
+  double tlim = 1.5*approx_tol_;
+  int nlim = 100;  // Currently
+  double angtol = 5.0*anglim_;
+  bool found = false;
+  for (ki=0; ki<axis_par.size(); ki=kj)
+    {
+      for (kj=ki+1; kj<axis_par.size() && axis_par[kj]-axis_par[ki]<tlim; ++kj);
+      int num = 0;
+      for (size_t kr=ki; kr<kj; ++kr)
+	num += groups[kr]->numPoints();
+      if (num < nlim)
+	continue;
+      if (axis_range[ki].second <= ppar1+delta)
+	continue;
+      if (axis_range[ki].first >= ppar2-delta)
+	continue;
+
+      Point mid(0.0, 0.0, 0.0);
+      int nmb = 0;
+      for (size_t kr=ki; kr<kj; ++kr)
+	{
+	  int num2 = groups[kr]->numPoints();
+	  for (int ka=0; ka<num2; ++ka)
+	    {
+	      Vector3D pos0 = groups[kr]->getPoint(ka)->getPoint();
+	      Point pos(pos0[0], pos0[1], pos0[2]);
+	      double tpar = (pos - loc)*axis;
+	      if (tpar <= ppar1+delta || tpar >= ppar2-delta)
+		continue;
+	      Point vec = pos - loc;
+	      Point pos2 = loc + (vec*axis)*axis;
+	      mid += pos2;
+	      ++nmb;
+	    }
+	}
+
+      if (nmb == 0)
+	continue;
+      mid /= (double)nmb;
+      
+      shared_ptr<Plane> plane(new Plane(mid, axis, Cx));
+#ifdef DEBUG_SMALL
+      std::ofstream of("small_plane.g2");
+      plane->writeStandardHeader(of);
+      plane->write(of);
+#endif
+
+      // Check accuracy
+      double maxdist = 0.0, avdist = 0.0;
+      int num_in = 0, num2_in = 0;
+      double fac = 1.0/(double)(kj-ki);
+      for (size_t kr=ki; kr<kj; ++kr)
+	{
+	  double maxd, avd;
+	  int n_in, n2_in;
+	  vector<double> parvals;
+	  vector<pair<double,double> > distang;
+	  vector<RevEngPoint*> in, out;
+	  RevEngUtils::distToSurf(groups[kr]->pointsBegin(), groups[kr]->pointsEnd(),
+				  plane, approx_tol_, maxd, avd, n_in,
+				  n2_in, in, out, parvals, distang, angtol);
+	  maxdist = std::max(maxdist, maxd);
+	  avdist += fac*avd;
+	  num_in += n_in;
+	  num2_in += n2_in;
+	}
+      int surf_flag = groups[ki]->defineSfFlag(num, 0, approx_tol_, num_in,
+					       num2_in, avdist, false);
+      if (surf_flag < NOT_SET)
+	{
+	  sfs.push_back(plane);
+	  found = true;
+	}
+    }
+  return found;
+}
+ 
+
+//===========================================================================
+void RevEng::adaptToMainAxis()
+//===========================================================================
+{
+  doAdaptToAxis();
+
+#ifdef DEBUG
+  std::cout << "Post merge similar. Number of regions: " << regions_.size() << std::endl;
+
+  checkConsistence("Regions11_1");
+
+  if (regions_.size() > 0)
+    {
+      std::cout << "Regions 11_1" << std::endl;
+      std::ofstream of("regions11_1.g2");
+      std::ofstream ofm("mid_regions11_1.g2");
+      std::ofstream ofs("small_regions11_1.g2");
+      writeRegionStage(of, ofm, ofs);
+      std::ofstream of0("regions11_1_helix.g2");
+      for (int ki=0; ki<(int)regions_.size(); ++ki)
+	{
+	  if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+	    {
+	      regions_[ki]->writeRegionInfo(of0);
+	      if (regions_[ki]->hasSurface())
+		regions_[ki]->writeSurface(of0);
+	    }
+	}
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf11_1.g2");
+	  writeRegionWithSurf(of);
+	}
+    }
+#endif
+	  
+#ifdef DEBUG
+  std::ofstream ofu1("unresolved2.g2");
+  for (size_t kr=0; kr<regions_.size(); ++kr)
+    {
+      if (regions_[kr]->hasSurface())
+	continue;
+      if (regions_[kr]->hasAssociatedBlend())
+	continue;
+      regions_[kr]->writeRegionPoints(ofu1);
+    }
+#endif
+
+  // Merge adjacent edges if possible
+  double eps = 1.0e-9;
+  for (int ka=0; ka<(int)edges_.size(); ++ka)
+    {
+      RevEngRegion* adj1[2];
+      edges_[ka]->getAdjacent(adj1[0], adj1[1]);
+      for (int kb=ka+1; kb<(int)edges_.size(); ++kb)
+	{
+	  RevEngRegion* adj2[2];
+	  edges_[kb]->getAdjacent(adj2[0], adj2[1]);
+
+	  if ((adj1[0] == adj2[0] || adj1[0] == adj2[1]) &&
+	      (adj1[1] == adj2[0] || adj1[1] == adj2[1]))
+	    {
+	      double par1, par2;
+	      bool adjacent = edges_[ka]->isAdjacent(edges_[kb].get(),
+						     approx_tol_, par1, par2);
+	      if (adjacent)
+		{
+#ifdef DEBUG_BLEND
+		  std::ofstream of("adj_blend.g2");
+		  adj1[0]->writeRegionPoints(of);
+		  adj1[1]->writeRegionPoints(of);
+		  vector<shared_ptr<ParamCurve> > cvs1 = edges_[ka]->getSpaceCurves();
+		  for (size_t kh=0; kh<cvs1.size(); ++kh)
+		    {
+		      cvs1[kh]->writeStandardHeader(of);
+		      cvs1[kh]->write(of);
+		    }
+		  vector<shared_ptr<ParamCurve> > cvs2 = edges_[kb]->getSpaceCurves();
+		  for (size_t kh=0; kh<cvs2.size(); ++kh)
+		    {
+		      cvs2[kh]->writeStandardHeader(of);
+		      cvs2[kh]->write(of);
+		    }
+	      
+#endif
+		  // Check for seam of adjacent surface
+		  shared_ptr<ParamSurface> surf1 = adj1[0]->getSurface(0)->surface();
+		  shared_ptr<ParamSurface> surf2 = adj1[1]->getSurface(0)->surface();
+		  shared_ptr<ElementarySurface> elem1 =
+		    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+		  shared_ptr<ElementarySurface> elem2 =
+		    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+		  bool close1=false, close2=false, close3=false, close4=false;
+		  if (elem1.get())
+		    elem1->isClosed(close1, close2);
+		  if (elem2.get())
+		    elem2->isClosed(close3, close4);
+		  Point pos = edges_[ka]->point(par1);
+		  if (close1 || close2)
+		    {
+		      RectDomain dom = elem1->containingDomain();
+		      double upar, vpar, dist;
+		      Point close_pt;
+		      elem1->closestBoundaryPoint(pos, upar, vpar, close_pt, dist, eps);
+		      if (dist < approx_tol_)
+			{
+			  if (close1 && (fabs(upar-dom.umin()) < eps || fabs(dom.umax()-upar) < eps))
+			    adjacent = false;
+			  if (close2 && (fabs(vpar-dom.vmin()) < eps || fabs(dom.vmax()-vpar) < eps))
+			    adjacent = false;
+			}
+		    }
+		  
+		  if (close3 || close4)
+		    {
+		      RectDomain dom = elem2->containingDomain();
+		      double upar, vpar, dist;
+		      Point close_pt;
+		      elem2->closestBoundaryPoint(pos, upar, vpar, close_pt, dist, eps);
+		      if (dist < approx_tol_)
+			{
+			  if (close3 && (fabs(upar-dom.umin()) < eps || fabs(dom.umax()-upar) < eps))
+			    adjacent = false;
+			  if (close4 && (fabs(vpar-dom.vmin()) < eps || fabs(dom.vmax()-vpar) < eps))
+			    adjacent = false;
+			}
+		    }
+		}
+
+	      if (adjacent)
+		{
+		  double t1 = edges_[ka]->startparam();
+		  double t2 = edges_[ka]->endparam();
+		  if (fabs(par1-t1) < fabs(par2-t2))
+		    std::swap(edges_[ka], edges_[kb]);
+		  bool done = edges_[ka]->append(edges_[kb].get(), approx_tol_);
+		  if (done)
+		    {
+		      edges_.erase(edges_.begin()+kb);
+		      kb--;
+		    }
+		  int stop_break0 = 1;
+		}
+	    }
+	}
+    }
+
+   int stop_break = 1;
+  
+}
+
+//===========================================================================
+void RevEng::doAdaptToAxis()
+//===========================================================================
+{
+  vector<SurfaceProperties> sfprop;
+  collectAxis(sfprop);
+
+  // Sort surfaces into groups with roughly the same axis
+  double epsang = 0.05;
+  double eps = 1.0e-4;
+  vector<DirectionCone> axis_cone;
+  vector<vector<size_t> > group_ixs;
+  vector<int> num_pts;
+  for (size_t ki=0; ki<sfprop.size(); ++ki)
+    {
+      if (sfprop[ki].type_ == Class_Sphere)
+	continue;   // Sphere centre is relevant, axis is derived
+      size_t kr;
+      for (kr=0; kr<axis_cone.size(); ++kr)
+	{
+	  Point centre = axis_cone[kr].centre();
+	  if (sfprop[ki].dir_*centre < 0.0)
+	    sfprop[ki].dir_ *= -1;
+	  Point axis = sfprop[ki].dir_;
+	  double angle = axis_cone[kr].unionAngle(axis);
+	  if (angle <= epsang)
+	    {
+	      axis_cone[kr].addUnionWith(axis);
+	      group_ixs[kr].push_back(ki);
+	      num_pts[kr] += sfprop[ki].num_points_;
+	      break;
+	    }
+	}
+      if (kr == axis_cone.size())
+	{
+	  DirectionCone cone(sfprop[ki].dir_);
+	  axis_cone.push_back(cone);
+	  vector<size_t> ixs;
+	  ixs.push_back(ki);
+	  group_ixs.push_back(ixs);
+	  num_pts.push_back(sfprop[ki].num_points_);
+	}
+    }
+
+  // Sort according to number of points associated to the surface
+  for (size_t ki=0; ki<num_pts.size(); ++ki)
+    for (size_t kj=ki+1; kj<num_pts.size(); ++kj)
+      {
+	if (num_pts[kj] > num_pts[ki])
+	  {
+	    std::swap(num_pts[ki], num_pts[kj]);
+	    std::swap(axis_cone[ki], axis_cone[kj]);
+	    std::swap(group_ixs[ki], group_ixs[kj]);
+	  }
+      }
+
+  // For each group, ecompute axis direction based on reliability of surface 
+  // type and number of points associated to the surface
+  double planefac = 1.0;
+  double cylfac = 0.75;
+  double conefac = 0.6;
+  double torfac = 0.4;
+  vector<Point> axes(group_ixs.size());
+  for (size_t ki=0; ki<group_ixs.size(); ++ki)
+    {
+      Point curr(0.0, 0.0, 0.0);
+      for (size_t kj=0; kj<group_ixs[ki].size(); ++kj)
+	{
+	  Point curr2 = sfprop[group_ixs[ki][kj]].dir_;
+	  double fac = planefac;
+	  if (sfprop[group_ixs[ki][kj]].type_ == Class_Cylinder)
+	    fac = cylfac;
+	  else if (sfprop[group_ixs[ki][kj]].type_ == Class_Cone)
+	    fac = conefac;
+	  else if (sfprop[group_ixs[ki][kj]].type_ == Class_Torus)
+	    fac = torfac;
+	  int num = sfprop[group_ixs[ki][kj]].num_points_;
+	  curr += (double)num*fac*curr2;
+	}
+      if (curr.length() < eps)
+	axes[ki] = axis_cone[ki].centre();
+      else
+	{
+	  curr.normalize();
+	  axes[ki] = curr;
+	}
+    }
+
+  adjustWithMainAxis(axes, num_pts); 
+
+  size_t num_axis = model_axis_.size();
+  vector<RevEngEdge*> nopar_edgs;
+  for (size_t ki=0; ki<num_pts.size(); ++ki)
+    {
+      int ix=-1;
+      double min_ang = M_PI;
+      for (int ka=0; ka<3; ++ka)
+	{
+	  double ang = axes[ki].angle(mainaxis_[ka]);
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang < min_ang)
+	    {
+	      min_ang = ang;
+	      ix = ka;
+	    }
+	}
+
+      Point Cx = mainaxis_[(ix+2)%3].cross(axes[ki]);
+      Cx.normalize();
+
+      AxisInfo curr_axis(axes[ki]);
+      model_axis_.push_back(curr_axis);
+      
+     // Identify surfaces with the "same" axis including location
+      vector<vector<int> > plane_adapt;
+      vector<int> num_adapt1;
+      for (size_t kj=0; kj<group_ixs[ki].size(); ++kj)
+	{
+	  if (sfprop[group_ixs[ki][kj]].type_  != Class_Plane)
+	    continue;
+
+	  shared_ptr<ParamSurface> surf = surfaces_[sfprop[group_ixs[ki][kj]].sfix_]->surface();
+	  shared_ptr<Plane> plane = dynamic_pointer_cast<Plane,ParamSurface>(surf);
+	  Point loc = plane->location();
+
+	  size_t kr, kh;
+	  for (kr=0; kr<plane_adapt.size(); ++kr)
+	    {
+	      for (kh=0; kh<plane_adapt[kr].size(); ++kh)
+		{
+		  shared_ptr<ParamSurface> surf2 = surfaces_[plane_adapt[kr][kh]]->surface();
+		  shared_ptr<Plane> plane2 = dynamic_pointer_cast<Plane,ParamSurface>(surf2);
+		  Point loc2 = plane2->location();
+		  double dd = fabs((loc-loc2)*axes[ki]);
+		  if (dd <= approx_tol_)
+		    break;
+		}
+	      if (kh < plane_adapt[kr].size())
+		break;
+	    }
+	  if (kr == plane_adapt.size())
+	    {
+	      vector<int> curr_adapt;
+	      curr_adapt.push_back(sfprop[group_ixs[ki][kj]].sfix_);
+	      plane_adapt.push_back(curr_adapt);
+	      num_adapt1.push_back(sfprop[group_ixs[ki][kj]].num_points_);
+	    }
+	  else
+	    {
+	      plane_adapt[kr].push_back(sfprop[group_ixs[ki][kj]].sfix_);
+	      num_adapt1[kr] += sfprop[group_ixs[ki][kj]].num_points_;
+	    }
+	}
+
+      for (size_t kj=0; kj<plane_adapt.size(); ++kj)
+	{
+	  Point location = planarFit(plane_adapt[kj], axes[ki]);
+	  model_axis_[num_axis+ki].addPlaneLocation(location, num_adapt1[kj]);
+	}
+      
+      // The other surfaces
+      vector<vector<int> > rotational_adapt;
+       vector<int> num_adapt2;
+      for (size_t kj=0; kj<group_ixs[ki].size(); ++kj)
+	{
+	  if (sfprop[group_ixs[ki][kj]].type_ == Class_Plane)
+	    continue;
+
+	  Point loc = sfprop[group_ixs[ki][kj]].loc_;
+	  size_t kr, kh;
+	  for (kr=0; kr<rotational_adapt.size(); ++kr)
+	    {
+	      for (kh=0; kh<rotational_adapt[kr].size(); ++kh)
+		{
+		  shared_ptr<ParamSurface> surf2 =
+		    surfaces_[rotational_adapt[kr][kh]]->surface();
+		  shared_ptr<ElementarySurface> elem =
+		    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+		  Point loc2 = elem->location();
+		  Point tmp = loc + ((loc2-loc)*axes[ki])*axes[ki];
+		  double dd = loc2.dist(tmp);
+		  if (dd <= approx_tol_)
+		    break;
+		}
+	      if (kh < rotational_adapt[kr].size())
+		break;
+	    }
+	  if (kr == rotational_adapt.size())
+	    {
+	      vector<int> curr_adapt;
+	      curr_adapt.push_back(sfprop[group_ixs[ki][kj]].sfix_);
+	      rotational_adapt.push_back(curr_adapt);
+	      num_adapt2.push_back(sfprop[group_ixs[ki][kj]].num_points_);
+	    }
+	  else
+	    {
+	      rotational_adapt[kr].push_back(sfprop[group_ixs[ki][kj]].sfix_);
+	      num_adapt2[kr] += sfprop[group_ixs[ki][kj]].num_points_;
+	    }
+	}
+
+      for (size_t kj=0; kj<rotational_adapt.size(); ++kj)
+	{
+	  Point location = rotationalFit(rotational_adapt[kj], axes[ki], Cx,
+					 nopar_edgs);
+	  if (location.dimension() > 0)
+	    model_axis_[num_axis+ki].addRotationalLocation(location, num_adapt2[kj]);
+	}
+    }
+
+  // Sort axes
+  vector<int> num_axis_pt(model_axis_.size());
+  for (size_t ki=0; ki<model_axis_.size(); ++ki)
+    {
+      int num_pt = 0;
+      for (size_t kj=0; kj<model_axis_[ki].plane_loc_.size(); ++kj)
+	num_pt += model_axis_[ki].plane_loc_[kj].second;
+      for (size_t kj=0; kj<model_axis_[ki].rotational_loc_.size(); ++kj)
+	num_pt += model_axis_[ki].rotational_loc_[kj].second;
+      num_axis_pt[ki] = num_pt;
+    }
+  
+   for (size_t ki=0; ki<model_axis_.size(); ++ki)
+    {
+      for (size_t kj=ki+1; kj<model_axis_.size(); ++kj)
+	if (num_axis_pt[ki] < num_axis_pt[kj])
+	  {
+	    std::swap(model_axis_[ki], model_axis_[kj]);
+	    std::swap(num_axis_pt[ki], num_axis_pt[kj]);
+	  }
+    }
+ 
+
+  if (nopar_edgs.size() > 0)
+    {
+      // Must split edge at seam
+      vector<shared_ptr<RevEngEdge> > added_edgs;
+      vector<shared_ptr<RevEngRegion> > added_regs;
+      vector<shared_ptr<HedgeSurface> > added_sfs;
+      for (size_t ki=0; ki<nopar_edgs.size(); ++ki)
+	{
+	  if (nopar_edgs[ki]->isClosed(approx_tol_))
+	    continue;  // Will be handled later
+	  nopar_edgs[ki]->splitAtSeam(approx_tol_, added_edgs, added_regs,
+				      added_sfs);
+	}
+      if (added_edgs.size() > 0)
+	edges_.insert(edges_.end(), added_edgs.begin(), added_edgs.end());
+      if (added_regs.size() > 0)
+	regions_.insert(regions_.end(), added_regs.begin(), added_regs.end());
+      if (added_sfs.size() > 0)
+	surfaces_.insert(surfaces_.end(), added_sfs.begin(), added_sfs.end());
+    }
+  
+  int stop_break = 1;
+}
+
+//===========================================================================
+void RevEng::adjustWithMainAxis(vector<Point>& axes, vector<int>& num_pts)
+//===========================================================================
+{
+  double angtol1 = 5.0*anglim_;
+  double angtol2 = 5.0*angtol1;
+  double pihalf = 0.5*M_PI;
+  double eps = 1.0e-6;
+
+  // Change sign of axis if in conflict with existing main axis
+  for (size_t ki=0; ki<axes.size(); ++ki)
+    {
+      for (int ka=0; ka<3; ++ka)
+	{
+	  double ang = axes[ki].angle(mainaxis_[ka]);
+	  ang = std::min(ang, M_PI-ang);
+	  double scpr = axes[ki]*mainaxis_[ka];
+	  if (scpr < 0.0 && ang<angtol2)
+	    axes[ki] *= -1.0;
+	}
+    }
+
+  vector<Point> axes2(axes.size());
+  for (size_t ki=0; ki<axes.size(); ++ki)
+    axes2[ki] = axes[ki];
+  
+  for (int ix1=(int)axes2.size()-1; ix1>=0; --ix1)
+    {
+      Vector3D axis1(axes2[ix1][0], axes2[ix1][1], axes2[ix1][2]);
+      for (int ix2=ix1-1; ix2>=0; --ix2)
+	{
+	  Vector3D axis2(axes2[ix2][0], axes2[ix2][1], axes2[ix2][2]);
+	  double ang = axes2[ix1].angle(axes2[ix2]);
+	  double ang2 = fabs(pihalf-ang);
+	  if (ang2 > eps && ang2 < angtol1)
+	    {
+	      // Adjust axes2 to achieve orthogonality
+	      Point vec = axes2[ix1].cross(axes2[ix2]);
+	      int sgn = (ang < pihalf) ? 1 : -1;
+	      double fac = 1.0/(double)(num_pts[ix1] + num_pts[ix2]);
+	      double ang3 = (double)num_pts[ix2]*fac*ang2;
+	      double ang4 = (double)num_pts[ix1]*fac*ang2;
+	      Matrix3D mat1, mat2;
+	      mat1.setToRotation(-sgn*ang3, vec[0], vec[1], vec[2]);
+	      mat2.setToRotation(sgn*ang4, vec[0], vec[1], vec[2]);
+	      Vector3D axis3 = mat1*axis1;
+	      Vector3D axis4 = mat2*axis2;
+	      axis3.normalize_checked();
+	      axis4.normalize_checked();
+	      axes2[ix1] = Point(axis3[0], axis3[1], axis3[2]);
+	      axes2[ix2] = Point(axis4[0], axis4[1], axis4[2]);
+	      int stop_break = 1;
+	    }
+	}
+    }
+
+  // Ensure orthogonality
+  int ix1 = 0;
+  int ix2 = -1, ix3 = -1;
+  for (int ka=1; ka<(int)axes2.size(); ++ka)
+    {
+      double ang = axes2[ix1].angle(axes2[ka]);
+      if (fabs(pihalf-ang) < angtol1)
+	{
+	  if (ix2 < 0)
+	    ix2 = ka;
+	  else if (ix3 < 0)
+	    {
+	      ix3 = ka;
+	      break;
+	    }
+	}
+    }
+
+  if (ix2 < 0)
+    {
+      ix2 = (int)axes2.size();
+      ix3 = (int)axes2.size()+1;
+      axes2.resize(axes2.size()+2);
+    }
+  else if (ix3 < 0)
+    {
+      ix3 = (int)axes2.size();
+      axes2.resize(axes2.size()+1);
+    }
+
+  if (ix3 > (int)axes.size())
+    {
+      int min_ix = 0;
+      double min_ang = fabs(axes2[ix1].angle(mainaxis_[0]));
+      for (int kb=1; kb<3; ++kb)
+	{
+	  double ang = fabs(axes2[ix1].angle(mainaxis_[kb]));
+	  if (ang < min_ang)
+	    {
+	      min_ix = kb;
+	      min_ang = ang;
+	    }
+	}
+
+      int min_ix2 = -1;
+      if (ix2 < (int)axes.size())
+	{
+	  min_ix2 = (min_ang == 0) ? 1 : 0;
+	  double min_ang2 = fabs(axes2[ix2].angle(mainaxis_[min_ix2]));
+	  for (int kb=0; kb<3; ++kb)
+	    {
+	      if (kb == min_ix || kb == min_ix2)
+		continue;
+	      double ang = fabs(axes2[ix2].angle(mainaxis_[kb]));
+	      if (ang < min_ang)
+		{
+		  min_ix2 = kb;
+		  min_ang2 = ang;
+		}
+	    }
+	}
+      else
+	{
+	  for (int kb=0; kb<3; ++kb)
+	    if (kb != min_ix)
+	      {
+		axes2[ix2] = mainaxis_[kb];
+		min_ix2 = kb;
+		break;
+	      }
+	}
+
+      for (int kb=0; kb<3; ++kb)
+	if (kb != min_ix && kb != min_ix2)
+	  {
+	    axes2[ix3] = mainaxis_[kb];
+	    break;
+	  }
+    }
+
+  axes2[ix3] = axes2[ix1].cross(axes2[ix2]);
+  axes2[ix2] = axes2[ix3].cross(axes2[ix1]);
+
+  for (size_t ki=0; ki<axes.size(); ++ki)
+    axes[ki] = axes2[ki];
+
+  mainaxis_[0] = axes2[ix1];
+  mainaxis_[1] = axes2[ix2];
+  mainaxis_[2] = axes2[ix3];
+}
+
+//===========================================================================
+Point RevEng::planarFit(vector<int>& sf_ix, Point axis)
+//===========================================================================
+{
+  double angtol = 5.0*anglim_;
+  
+  // Collect data points
+  vector<RevEngPoint*> points;
+  Point loc(0.0, 0.0, 0.0);
+  int num = 0;
+  for (size_t ki=0; ki<sf_ix.size(); ++ki)
+    {
+      HedgeSurface* surf = surfaces_[sf_ix[ki]].get();
+      shared_ptr<Plane> plane =
+	dynamic_pointer_cast<Plane,ParamSurface>(surf->surface());
+      if (!plane.get())
+	continue;
+      loc += plane->location();
+      num++;
+      vector<RevEngRegion*> reg = surf->getRegions();
+      for (size_t kj=0; kj<reg.size(); ++kj)
+	{
+	  points.insert(points.end(), reg[kj]->pointsBegin(),
+			reg[kj]->pointsEnd());
+	}
+    }
+  if (num > 0)
+    loc /= (double)num;
+  
+  shared_ptr<Plane> plane2 = RevEngUtils::planeWithAxis(points, axis,
+							loc, mainaxis_);
+
+  Point centre = plane2->location();
+  Point Cx = plane2->direction2();
+
+  for (size_t ki=0; ki<sf_ix.size(); ++ki)
+    {
+      HedgeSurface* surf = surfaces_[sf_ix[ki]].get();
+      shared_ptr<Plane> plane =
+	dynamic_pointer_cast<Plane,ParamSurface>(surf->surface());
+      if (!plane.get())
+	continue;
+      Point loc2 = plane->location();
+      loc2 -= ((loc2-centre)*axis)*axis;
+      shared_ptr<Plane> plane3(new Plane(loc2, axis, Cx));
+      if (plane->direction()*axis < 0.0)
+	plane3->swapParameterDirection();
+
+      vector<RevEngRegion*> reg = surf->getRegions();
+      double maxdist = 0.0, avdist = 0.0;
+      int num_in = 0, num2_in = 0, num_pt = 0;
+      double maxdist_init = 0.0, avdist_init = 0.0;
+      int num_in_init = 0, num2_in_init = 0;
+      vector<vector<double> > parvals(reg.size());
+      vector<vector<pair<double,double> > > dist_ang(reg.size());
+      vector<int> init_flag(reg.size());;
+      for (size_t kj=0; kj<reg.size(); ++kj)
+	{
+	  double maxd0, avd0;
+	  int num_in0, num2_in0;
+	  vector<RevEngPoint*> in0, out0;
+	  vector<pair<double,double> > distang0;
+	  vector<double> parvals0;
+	  RevEngUtils::distToSurf(reg[kj]->pointsBegin(), reg[kj]->pointsEnd(),
+				  plane3, approx_tol_, maxd0, avd0, num_in0, num2_in0,
+				  in0, out0, parvals0, distang0, angtol);
+	  int num0 = reg[kj]->numPoints();
+	  maxdist = std::max(maxdist, maxd0);
+	  avdist = (double)num_pt*avdist + (double)num0*avd0;
+	  num_in += num_in0;
+	  num2_in += num2_in0;
+	  parvals[kj] = parvals0;
+	  dist_ang[kj] = distang0;
+
+	  double maxd1, avd1;
+	  int num_in1, num2_in1;
+	  reg[kj]->getAccuracy(maxd1, avd1, num_in1, num2_in1);
+	  maxdist_init = std::max(maxdist_init, maxd1);
+	  avdist_init = (double)num_pt*avdist_init + (double)num0*avd1;
+	  num_in_init += num_in1;
+	  num2_in_init += num2_in1;
+	  num_pt += num0;
+	  init_flag[kj] = reg[kj]->getSurfaceFlag();
+	}
+      avdist /= (double)num_pt;
+      avdist_init /= (double)num_pt;
+      int surf_flag = reg[0]->defineSfFlag(num_pt, 0, approx_tol_, num_in,
+					   num2_in, avdist, false);
+      int surf_flag_init = reg[0]->defineSfFlag(num_pt, 0, approx_tol_, num_in_init,
+						num2_in_init, avdist_init, false);
+
+      double av_fac = 1.1;
+      double in_fac = 0.9;
+      if (surf_flag == 0 || surf_flag <= surf_flag_init ||
+	  (avdist < av_fac*avdist_init &&
+	   (double)num2_in > in_fac*(double)num2_in_init &&
+	   (double)num_in > in_fac*(double)num_in_init))
+	{
+	  // Replace surface
+	  for (size_t kj=0; kj<reg.size(); ++kj)
+	    {
+	      vector<RevEngEdge*> dummy;
+	      reg[kj]->updateSurfaceAndInfo(plane3, approx_tol_, angtol,
+					    parvals[kj], dist_ang[kj], dummy);
+	    }
+	}
+      
+      int stop_break = 1;
+    }
+  return centre;
+}
+
+//===========================================================================
+Point RevEng::rotationalFit(vector<int>& sf_ix, Point axis, Point Cx,
+			    vector<RevEngEdge*>& nopar_edgs)
+//===========================================================================
+{
+  double angtol = 5.0*anglim_;
+  Point dummy_loc;
+  
+  // Compute point on axis
+  Point loc(0.0, 0.0, 0.0);
+  int num = 0;
+  for (size_t ki=0; ki<sf_ix.size(); ++ki)
+    {
+      HedgeSurface* surf = surfaces_[sf_ix[ki]].get();
+      shared_ptr<ElementarySurface> elem =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf->surface());
+      if (!elem.get())
+	continue;
+      loc += elem->location();
+      num++;
+    }
+  if (num > 0)
+    loc /= (double)num;
+
+  // Add spheres with centre on axis
+  for (size_t ki=0; ki<surfaces_.size(); ++ki)
+    {
+      shared_ptr<ParamSurface> sf = surfaces_[ki]->surface();
+      if (sf->instanceType() == Class_Sphere)
+	{
+	  shared_ptr<Sphere> sphere = dynamic_pointer_cast<Sphere,ParamSurface>(sf);
+	  Point centre = sphere->location();
+	  Point tmp = loc + ((centre-loc)*axis)*axis;
+	  if (centre.dist(tmp) < approx_tol_)
+	    sf_ix.push_back((int)ki);
+	}
+    }
+
+  double udomain[2];
+  udomain[0] = std::numeric_limits<double>::max();
+  udomain[1] = std::numeric_limits<double>::lowest();
+  int num_pts = 0;
+  for (size_t ki=0; ki<sf_ix.size(); ++ki)
+    {
+      HedgeSurface* surf = surfaces_[sf_ix[ki]].get();
+      shared_ptr<ElementarySurface> elem =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf->surface());
+      if (!elem.get())
+	continue;
+      Point loc2 = elem->location();
+      Point loc2_2 = loc + ((loc2 - loc)*axis)*axis;
+
+      vector<RevEngPoint*> points;
+      vector<RevEngRegion*> reg = surf->getRegions();
+      for (size_t kj=0; kj<reg.size(); ++kj)
+	{
+	  points.insert(points.end(), reg[kj]->pointsBegin(),
+			reg[kj]->pointsEnd());
+	}
+
+      shared_ptr<ElementarySurface> elem2;
+      if (elem->instanceType() == Class_Cylinder)
+	elem2 = RevEngUtils::cylinderWithAxis(points, axis, Cx, loc2_2);
+      else if (elem->instanceType() == Class_Cone)
+	{
+	  double len = bbox_.low().dist(bbox_.high());
+	  elem2 = RevEngUtils::coneWithAxis(points, axis, Cx, loc2_2, len);
+	  if (!elem2.get())
+	    elem2 = elem;
+	}
+      else if (elem->instanceType() == Class_Torus)
+	elem2 = RevEngUtils::torusWithAxis(points, axis, Cx, loc2_2);
+      else if (elem->instanceType() == Class_Sphere)
+	elem2 = RevEngUtils::sphereWithAxis(points, axis, Cx, loc2_2);
+
+      double maxdist = 0.0, avdist = 0.0;
+      int num_in = 0, num2_in = 0, num_pt = 0;
+      double maxdist_init = 0.0, avdist_init = 0.0;
+      int num_in_init = 0, num2_in_init = 0;
+      vector<vector<double> > parvals(reg.size());
+      vector<vector<pair<double,double> > > dist_ang(reg.size());
+      vector<int> init_flag(reg.size());;
+      for (size_t kj=0; kj<reg.size(); ++kj)
+	{
+	  double maxd0, avd0;
+	  int num_in0, num2_in0;
+	  vector<RevEngPoint*> in0, out0;
+	  vector<pair<double,double> > distang0;
+	  vector<double> parvals0;
+	  RevEngUtils::distToSurf(reg[kj]->pointsBegin(), reg[kj]->pointsEnd(),
+				  elem2, approx_tol_, maxd0, avd0, num_in0, num2_in0,
+				  in0, out0, parvals0, distang0, angtol);
+	  int num0 = reg[kj]->numPoints();
+	  maxdist = std::max(maxdist, maxd0);
+	  avdist = (double)num_pt*avdist + (double)num0*avd0;
+	  num_in += num_in0;
+	  num2_in += num2_in0;
+	  parvals[kj] = parvals0;
+	  dist_ang[kj] = distang0;
+
+	  double maxd1, avd1;
+	  int num_in1, num2_in1;
+	  reg[kj]->getAccuracy(maxd1, avd1, num_in1, num2_in1);
+	  maxdist_init = std::max(maxdist_init, maxd1);
+	  avdist_init = (double)num_pt*avdist_init + (double)num0*avd1;
+	  num_in_init += num_in1;
+	  num2_in_init += num2_in1;
+	  num_pt += num0;
+	  init_flag[kj] = reg[kj]->getSurfaceFlag();
+	}
+      avdist /= (double)num_pt;
+      avdist_init /= (double)num_pt;
+
+      bool cyl_like = (elem->instanceType() == Class_Cylinder ||
+		       elem->instanceType() == Class_Cone);
+      int surf_flag = reg[0]->defineSfFlag(num_pt, 0, approx_tol_, num_in,
+					   num2_in, avdist, cyl_like);
+      int surf_flag_init = reg[0]->defineSfFlag(num_pt, 0, approx_tol_, num_in_init,
+						num2_in_init, avdist_init, cyl_like);
+
+      double av_fac = 1.1;
+      double in_fac = 0.9;
+      if (surf_flag == 0 || surf_flag <= surf_flag_init ||
+	  (avdist < av_fac*avdist_init &&
+	   (double)num2_in > in_fac*(double)num2_in_init &&
+	   (double)num_in > in_fac*(double)num_in_init))
+	{
+	  // Replace surface
+	  for (size_t kj=0; kj<reg.size(); ++kj)
+	    {
+	      reg[kj]->updateSurfaceAndInfo(elem2, approx_tol_, angtol,
+					    parvals[kj], dist_ang[kj],
+					    nopar_edgs);
+	    }
+	}
+      for (size_t kj=0; kj<reg.size(); ++kj)
+	{
+	  num_pts += reg[kj]->numPoints();
+	  double dom[4];
+	  reg[kj]->getDomain(dom);
+	  udomain[0] = std::min(udomain[0], dom[0]);
+	  udomain[1] = std::max(udomain[1], dom[1]);
+	}      
+      int stop_break = 1;
+    }
+
+  double seclim = 0.125*M_PI;
+  if (num_pts < min_point_region_ && udomain[1]-udomain[0] < seclim)
+    return dummy_loc;
+  return loc;
+}
+
+
+//===========================================================================
+void RevEng::collectAxis(vector<SurfaceProperties>& sfprop)
+//===========================================================================
+{
+  for (size_t ki=0; ki<surfaces_.size(); ++ki)
+    {
+      int code;
+      ClassType type = surfaces_[ki]->instanceType(code);
+      ClassType type2 = Class_Unknown;
+      shared_ptr<ParamSurface> surf = surfaces_[ki]->surface();
+      shared_ptr<ElementarySurface> elemsf =
+	dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+
+      RevEngRegion *reg0 = surfaces_[ki]->getRegion(0);
+      if (!reg0)
+	continue;  // Surface should have been remove prior to this!!!
+      
+      if (reg0->hasBlendEdge())
+	continue;  // Derived information
+      
+      if (reg0->hasAssociatedBlend())
+	continue;  // Outdated information
+      
+      int nreg = surfaces_[ki]->numRegions();
+      int num_pts = 0;
+      shared_ptr<ParamSurface> primary;
+;
+      int num_pt_primary = 0;
+      bool prefer_base = false;  // Maybe check accuracy between surface
+      // and primary surface later. Can also use history information about
+      // surface updates
+      bool type_cyl = (type == Class_Cylinder || type == Class_Cone);
+      int surfflag = 0;
+      for (int ka=0; ka<nreg; ++ka)
+	{
+	  RevEngRegion *reg = surfaces_[ki]->getRegion(ka);
+	  primary = reg->getBase();
+	  int num = reg->numPoints();
+	  num_pts += num;
+	  double maxd, avd;
+	  int num_in, num2_in;
+	  reg->getAccuracy(maxd, avd, num_in, num2_in);
+	  int surfflag0 = reg->defineSfFlag(num, 0, approx_tol_, num2_in,
+					   num2_in, avd, type_cyl);
+	  surfflag = std::max(surfflag, surfflag0);
+	  vector<shared_ptr<ElementarySurface> > sfs(primary.get() ? 2: 1);
+	  sfs[0] = elemsf;
+	  if (primary.get())
+	    sfs[1] = dynamic_pointer_cast<ElementarySurface,ParamSurface>(primary);
+	  double maxd2, avd2;
+	  int num_in2, num2_in2, surfflag2;
+	  int ix = reg->checkSurfaceAccuracy(sfs, approx_tol_, 5.0*anglim_, maxd2,
+					     avd2, num_in2, num2_in2, surfflag2);
+	  
+	  if (reg->hasBaseSf() && num > num_pt_primary)
+	    {
+	      double maxdp, avdp;
+	      int num_inp, num2_inp;
+	      reg->getBaseDist(maxdp, avdp, num_inp, num2_inp);
+	      if (num_inp > num/2 && avdp < approx_tol_)
+		{
+		  num_pt_primary = num;
+		}
+	    }
+	}
+      if (primary.get())
+	type2 = primary->instanceType();
+      
+      if ((!elemsf.get()) || prefer_base)
+	{
+	  if (primary.get())
+	    elemsf =
+	      dynamic_pointer_cast<ElementarySurface,ParamSurface>(primary);
+	}
+      if (!elemsf.get())
+	continue;
+      
+      Point loc, dir;
+      double rad1, rad2;
+      loc = elemsf->location();
+      dir = elemsf->direction();
+      rad1 = elemsf->radius(0.0, 0.0);   // Not good enough for cones
+      rad2 = elemsf->radius2(0.0, 0.0);   // Not good enough for cones
+      SurfaceProperties currprop((int)ki, type, num_pts, surfflag, dir, loc, 
+				 type2, rad1, rad2);
+      sfprop.push_back(currprop);
+    }
+}
+
+
+//===========================================================================
+void RevEng::trimSurfaces()
+//===========================================================================
+{
+  double angtol = 5.0*anglim_;
+#ifdef DEBUG_TRIM
+  std::ofstream of0("trimmedsfs.g2");
+#endif  
+  // Missing edges between surfaces
+  size_t num_edg = edges_.size();
+  recognizeEdges(true);
+
+  vector<RevEngRegion*> out_regs;
+  vector<HedgeSurface*> out_sfs;
+  for (size_t kj=0; kj<edges_.size(); ++kj)
+    {
+      int type = edges_[kj]->getType();
+      if (type == NOT_BLEND)
+	edges_[kj]->setTrimCurves(approx_tol_, angtol, out_regs, out_sfs);
+    }
+  if (out_regs.size() > 0 || out_sfs.size() > 0)
+    {
+      int ix = -1;
+      updateRegionsAndSurfaces(ix, out_regs, out_sfs);
+    }
+#ifdef DEBUG_TRIM
+      vector<shared_ptr<ftEdge> > trim_edgs1;
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  // if (regions_[kr]->numPoints() == 0)
+	  //   std::cout << "Finished set blend boundaries, empty region, ki=" << kr << ", region: " << regions_[kr].get() << std::endl;
+	  int num = regions_[kr]->numTrimEdges();
+	  if (num > 0)
+	    {
+	      vector<shared_ptr<ftEdge> > curr_edgs = regions_[kr]->getTrimEdges();
+	      trim_edgs1.insert(trim_edgs1.end(), curr_edgs.begin(), curr_edgs.end());
+	    }
+	}
+      if (trim_edgs1.size() > 0)
+	{
+	  std::ofstream oft1("trim_edges_1.g2");
+	  for (size_t kr=0; kr<trim_edgs1.size(); ++kr)
+	    {
+	      shared_ptr<ParamCurve> tmp = trim_edgs1[kr]->geomCurve();
+	      shared_ptr<CurveOnSurface> tmp2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(tmp);
+	      shared_ptr<ParamCurve> tmp3 = tmp2->spaceCurve();
+	      tmp3->writeStandardHeader(oft1);
+	      tmp3->write(oft1);
+	    }
+	}
+#endif
+      //#if 0
+   for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      if (!regions_[ki]->hasSurface())
+	continue;
+#ifdef DEBUG_TRIM
+      std::ofstream of("trimreg.g2");
+      regions_[ki]->writeRegionPoints(of);
+#endif
+
+      if (regions_[ki]->numTrimEdges() == 0)
+	continue;
+      if (regions_[ki]->hasBlendEdge())
+	continue;
+      regions_[ki]->extendBoundaries(mean_edge_len_, min_point_region_,
+				     approx_tol_, angtol, mainaxis_);
+    }
+   //#endif      
+#ifdef DEBUG_TRIM
+      vector<shared_ptr<ftEdge> > trim_edgs2;
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  // if (regions_[kr]->numPoints() == 0)
+	  //   std::cout << "Finished set blend boundaries, empty region, ki=" << kr << ", region: " << regions_[kr].get() << std::endl;
+	  int num = regions_[kr]->numTrimEdges();
+	  if (num > 0)
+	    {
+	      vector<shared_ptr<ftEdge> > curr_edgs = regions_[kr]->getTrimEdges();
+	      trim_edgs2.insert(trim_edgs2.end(), curr_edgs.begin(), curr_edgs.end());
+	    }
+	}
+      if (trim_edgs2.size() > 0)
+	{
+	  std::ofstream oft2("trim_edges_2.g2");
+	  for (size_t kr=0; kr<trim_edgs2.size(); ++kr)
+	    {
+	      shared_ptr<ParamCurve> tmp = trim_edgs2[kr]->geomCurve();
+	      shared_ptr<CurveOnSurface> tmp2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(tmp);
+	      shared_ptr<ParamCurve> tmp3 = tmp2->spaceCurve();
+	      tmp3->writeStandardHeader(oft2);
+	      tmp3->write(oft2);
+	    }
+	}
+#endif
+
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      bool trimmed;
+      if (!regions_[ki]->hasSurface())
+	continue;
+#ifdef DEBUG_TRIM
+      std::ofstream of2("trimreg2.g2");
+      regions_[ki]->writeRegionPoints(of2);
+#endif
+      if (regions_[ki]->numTrimEdges() == 0)
+	{
+	  trimmed = true;
+	  regions_[ki]->getSurface(0)->trimWithPoints(approx_tol_);
+	}
+      else
+	{
+	  try {
+	    trimmed = regions_[ki]->trimSurface(approx_tol_);
+	  }
+	  catch (...)
+	    {
+	      trimmed = regions_[ki]->getSurface(0)->trimWithPoints(approx_tol_);
+#ifdef DEBUG_TRIM
+	      std::cout << ki << " trim with points " << std::endl;
+#endif
+	    }
+	  if (!trimmed)
+	    {
+	      trimmed = regions_[ki]->getSurface(0)->trimWithPoints(approx_tol_);
+#ifdef DEBUG_TRIM
+	      std::cout << ki << " trim with points " << std::endl;
+#endif
+	    }
+	}
+      
+#ifdef DEBUG_TRIM
+      if (trimmed)
+	{
+	  shared_ptr<ParamSurface> tsurf = regions_[ki]->getSurface(0)->surface();
+	  shared_ptr<BoundedSurface> bdsurf = dynamic_pointer_cast<BoundedSurface,ParamSurface>(tsurf);
+	  if (bdsurf.get())
+	    {
+	      int valid_state;
+	      bool valid = bdsurf->isValid(valid_state);
+	      std::cout << "BoundedSurf " << ki << " is valid? " << valid << " " << valid_state << std::endl;
+	    }
+	  tsurf->writeStandardHeader(of2);
+	  tsurf->write(of2);
+	}
+#endif
+	
+    }
+#ifdef DEBUG
+   std::cout << "Finished trim surf, regions: " << regions_.size() << ", surfaces: " << surfaces_.size() << std::endl;
+   if (regions_.size() > 0)
+    {
+      std::cout << "Regions13" << std::endl;
+      std::ofstream of("regions13.g2");
+      std::ofstream ofm("mid_regions13.g2");
+      std::ofstream ofs("small_regions13.g2");
+      writeRegionStage(of, ofm, ofs);
+      std::ofstream of0("regions13_helix.g2");
+      for (int ki=0; ki<(int)regions_.size(); ++ki)
+	{
+	  if (regions_[ki]->getSurfaceFlag() == PROBABLE_HELIX)
+	    {
+	      regions_[ki]->writeRegionInfo(of0);
+	      if (regions_[ki]->hasSurface())
+		regions_[ki]->writeSurface(of0);
+	    }
+	}
+      if (surfaces_.size() > 0)
+	{
+	  std::ofstream of("regsurf13.g2");
+	  writeRegionWithSurf(of);
+	}
+     }
+   
+   if (edges_.size() > 0)
+     {
+       std::ofstream ofe("edges13.g2");
+       writeEdgeStage(ofe);
+     }
+#endif
+  
+#if 0
+#ifdef DEBUG
+  std::ofstream of1("surfbd.g2");
+#endif
+  for (size_t ki=0; ki<surfaces_.size(); ++ki)
+    {
+      // Restrict unbounded surfaces
+      surfaces_[ki]->ensureSurfaceBounded();
+#ifdef DEBUG
+      surfaces_[ki]->surface()->writeStandardHeader(of1);
+      surfaces_[ki]->surface()->write(of1);
+#endif
+    }
+
+#ifdef DEBUG
+  std::ofstream of3("trimsurfs.g2");
+#endif
+  for (size_t ki=0; ki<surfaces_.size(); ++ki)
+    {
+      surfaces_[ki]->trimWithPoints(approx_tol_);
+
+#ifdef DEBUG
+      surfaces_[ki]->surface()->writeStandardHeader(of3);
+      surfaces_[ki]->surface()->write(of3);
+#endif
+      int stop1 = 1;
+    }
+#endif
+  int stop_break = 1;
+}
+
+ //===========================================================================
+shared_ptr<SurfaceModel> RevEng::createModel()
+//===========================================================================
+{
+  // Ensure bounded surfaces
+  for (size_t ki=0; ki<surfaces_.size(); ++ki)
+    {
+      shared_ptr<ParamSurface> surf = surfaces_[ki]->surface();
+      if (!surf->isBounded())
+	{
+	  RevEngRegion *reg = surfaces_[ki]->getRegion(0);
+	  double dom[4];
+	  reg->getDomain(dom);
+	  shared_ptr<Plane> plane = dynamic_pointer_cast<Plane,ParamSurface>(surf);
+	  shared_ptr<Cylinder> cyl = dynamic_pointer_cast<Cylinder,ParamSurface>(surf);
+	  shared_ptr<Cone> cone = dynamic_pointer_cast<Cone,ParamSurface>(surf);
+	  if (plane.get())
+	    plane->setParameterBounds(dom[0], dom[2], dom[1], dom[3]);
+	  else if (cyl.get())
+	    cyl->setParamBoundsV(dom[2], dom[3]);
+	  else if (cone.get())
+	    cone->setParamBoundsV(dom[2], dom[3]);
+ 	}
+    }
+
+  double gap_tol = std::max(10.0*int_tol_, 0.01*approx_tol_);
+  vector<shared_ptr<ftSurface> > tmpsfs(surfaces_.begin(), surfaces_.end());
+  sfmodel_ = shared_ptr<SurfaceModel>(new SurfaceModel(approx_tol_, gap_tol,
+						       10*gap_tol, anglim_, 10*anglim_,
+						       tmpsfs));
+#ifdef DEBUG_MODEL
+  int num_bd = sfmodel_->nmbBoundaries();
+  if (num_bd > 0)
+    {
+      std::ofstream of1("model_boundaries.g2");
+      for (int ka=0; ka<num_bd; ++ka)
+	{
+	  ftCurve bd = sfmodel_->getBoundary(ka);
+	  int num_seg = bd.numSegments();
+	  for (int kb=0; kb<num_seg; ++kb)
+	    {
+	      shared_ptr<ParamCurve> cv = bd.segment(kb).spaceCurve();
+	      cv->writeStandardHeader(of1);
+	      cv->write(of1);
+	    }
+	}
+    }
+  
+#endif
+  return sfmodel_;
+}
+
+ //===========================================================================
+void RevEng::initParameters()
+//===========================================================================
+{
+  // Set default parameters
+  min_next_ = 10;  // Minimum number of neighbouring points
+  max_next_ = 500; //std::min(80, tri_sf_->size()/200); //500;
+  rfac_ = 6.0; //3.0;  // Factor for radius in which to search for neighbouring points
+  cfac_ = 8.0;  // Edge points from curvature is given by
+  // cfac_ times the average length of triangulation edges in a vertex
+  norm_plane_lim_= 0.005; // Limit for when the cone angle corresponding
+  // to triangle normals indicate an edge
+  zero_H_ = 0.005; //0.001; //0.0001;  // When mean curvature is considered zero
+  zero_K_ = 0.005; //0.001; //0.0001;  // When Gauss curvature is considered zero
+  norm_ang_lim_ = 0.1*M_PI; // Limit for when the cone angle corresponding
+    // to triangle normals indicate an edge
+  min_point_region_ = 200; //50; //10;  // Should be updated with regard to the total
+  // number of points
+  approx_tol_ = 0.001;  // Very preliminary
+  int_tol_ = 1.0e-6;
+  anglim_ = 0.01;
+  max_nmb_outlier_ = 3;
+  
+  model_character_  = 2;
+  prefer_elementary_ = 0;
+  mainaxis_[0] = Point(1.0, 0.0, 0.0);
+  mainaxis_[1] = Point(0.0, 1.0, 0.0);
+  mainaxis_[2] = Point(0.0, 0.0, 1.0);
+}
+
+ //===========================================================================
+void RevEng::updateParameters()
+//===========================================================================
+{
+  if (model_character_ == SMOOTH)
+    {
+      rfac_ = 4.0;
+    }
+  else if (model_character_ == MEDIUM_ROUGH)
+    {
+      zero_H_ = 0.007;
+      zero_K_ = 0.007;
+    }
+  else
+    {
+      rfac_ = 6.0;
+      zero_H_ = 0.01;
+      zero_K_ = 0.01;
+      anglim_ = 0.02;
+    }
+}
+
+ //===========================================================================
+int RevEng::setSmallRegionNumber()
+//===========================================================================
+{
+  vector<int> nmb_pt_reg(regions_.size());
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    nmb_pt_reg[ki] = regions_[ki]->numPoints();
+
+  std::sort(nmb_pt_reg.begin(), nmb_pt_reg.end());
+  int tot_num = tri_sf_->size();
+  int num_reg = (int)regions_.size();
+  int idel = tot_num/num_reg;
+  int min_num = std::min(10, tot_num/20);
+  int ixmax = (int)(0.99*num_reg);
+  int Q4 = 3*num_reg/4;
+  int max_num = std::max(min_num, nmb_pt_reg[ixmax]);
+  int Q4_num = nmb_pt_reg[Q4];
+  max_num = std::min(max_num, 10*idel);
+  int ixdel = std::max(num_reg/100, 2);
+  int prev = nmb_pt_reg[0], prev0 = 0;
+  int ix;
+  int fac = 2;
+  int min_jump = std::min(idel, Q4_num); //2;
+  for (ix=ixdel; ix<num_reg; ix+=ixdel)
+    {
+      int diff = nmb_pt_reg[ix] - prev;
+      if (diff > fac*(std::max(min_jump, prev-prev0)))
+	break;
+      if (diff > 0)
+	prev0 = prev;
+      prev = nmb_pt_reg[ix];
+    }
+  ix = std::min(ix, ixmax);
+      
+  int num = std::max(min_num, std::min(nmb_pt_reg[ix], max_num/2));
+  return num;
+}
+
+
+ //===========================================================================
+void RevEng::checkConsistence(std::string text) const
+//===========================================================================
+{
+  for (int ki=0; ki<(int)regions_.size(); ++ki)
+    {
+      vector<RevEngRegion*> adjacent;
+      regions_[ki]->getAdjacentRegions(adjacent);
+      for (size_t kj=0; kj<adjacent.size(); ++kj)
+	{
+	  size_t kr;
+	  for (kr=0; kr<regions_.size(); ++kr)
+	    if (adjacent[kj] == regions_[kr].get())
+	      break;
+	  if (kr == regions_.size())
+	    std::cout << text << ", Obsolete region pointer, ki=" << ki << ", kj=" << kj << ". Region: " << adjacent[kj] << std::endl;
+	}
+    }
+  for (int ki=0; ki<(int)surfaces_.size(); ++ki)
+    {
+      int numreg = surfaces_[ki]->numRegions();
+      for (int ka=0; ka<numreg; ++ka)
+	{
+	  RevEngRegion *reg = surfaces_[ki]->getRegion(ka);
+	  size_t kr;
+	  for (kr=0; kr<regions_.size(); ++kr)
+	    if (reg == regions_[kr].get())
+	      break;
+	  if (kr == regions_.size())
+	    std::cout << text << ", surface 1. Obsolete region pointer, ki=" << ki << ", ka=" << ka << ". Region: " << reg << std::endl;
+	  vector<RevEngRegion*> adjacent;
+	  reg->getAdjacentRegions(adjacent);
+	  for (size_t kj=0; kj<adjacent.size(); ++kj)
+	    {
+	      size_t kr;
+	      for (kr=0; kr<regions_.size(); ++kr)
+		if (adjacent[kj] == regions_[kr].get())
+		  break;
+	      if (kr == regions_.size())
+		std::cout << text << ", surface. Obsolete region pointer, ki=" << ki << ", kj=" << kj << ". Region: " << adjacent[kj] << std::endl;
+	    }
+	}
+    }
+}
+
+ //===========================================================================
+void RevEng::storeClassified(ostream& os) const
+//===========================================================================
+{
+  storeParams(os);
+  int nmbpts = tri_sf_->size();
+  os << nmbpts << std::endl;
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ki]);
+      pt->store(os);
+    }
+}
+
+ //===========================================================================
+void RevEng::readClassified(istream& is)
+//===========================================================================
+{
+  readParams(is);
+  int nmbpts;
+  is >> nmbpts;
+  tri_sf_ = shared_ptr<ftPointSet>(new ftPointSet());
+  vector<vector<int> > next_ix(nmbpts);
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      shared_ptr<RevEngPoint> vertex(new RevEngPoint());
+      vertex->read(is, next_ix[ki]);
+      tri_sf_->addEntry(vertex);
+    }
+
+  // Add next information
+  for (int ki=0; ki<nmbpts; ++ki)
+    {
+      ftSamplePoint* pt1 = (*tri_sf_)[ki];
+      for (size_t kr=0; kr<next_ix[ki].size(); ++kr)
+	{
+	  int ix = next_ix[ki][kr];
+	  ftSamplePoint* pt2 = (*tri_sf_)[ix];
+	  pt1->addNeighbour(pt2);
+	}
+    }
+
+  max_next_ = std::min(80, tri_sf_->size()/200);
+  max_next_ = std::max(2*min_next_, max_next_);
+  setBoundingBox();
+}
+
+ //===========================================================================
+void RevEng::storeGrownRegions(ostream& os)
+//===========================================================================
+{
+  storeClassified(os);
+  os << surfaces_.size() << std::endl;
+  for (int ka=0; ka<(int)surfaces_.size(); ++ka)
+    {
+      surfaces_[ka]->setId(ka);
+      surfaces_[ka]->store(os);
+    }
+  os << regions_.size() << std::endl;
+  for (size_t ki=0; ki<regions_.size(); ++ki)
+    {
+      regions_[ki]->setId((int)ki);
+      regions_[ki]->store(os);
+    }
+
+  os << single_points_.size() << std::endl;
+  for (size_t ki=0; ki<single_points_.size(); ++ki)
+    os << single_points_[ki]->getIndex() << " ";
+  os << std::endl;
+  
+  os << edges_.size() << std::endl;
+  for (int ka=0; ka<(int)edges_.size(); ++ka)
+    {
+      edges_[ka]->setId(ka);
+      edges_[ka]->store(os);
+    }
+
+  os << model_axis_.size() << std::endl;
+  for (size_t ki=0; ki<model_axis_.size(); ++ki)
+    {
+      os << model_axis_[ki].axis_ << " " << model_axis_[ki].plane_loc_.size();
+      os <<  " " << model_axis_[ki].rotational_loc_.size() << std::endl;
+      for (size_t kj=0; kj<model_axis_[ki].plane_loc_.size(); ++kj)
+	{
+	  os << model_axis_[ki].plane_loc_[kj].first << " ";
+	  os << model_axis_[ki].plane_loc_[kj].second << " ";
+	}
+      os << std::endl;
+      for (size_t kj=0; kj<model_axis_[ki].rotational_loc_.size(); ++kj)
+	{
+	  os << model_axis_[ki].rotational_loc_[kj].first << " ";
+	  os << model_axis_[ki].rotational_loc_[kj].second << " ";
+	}
+      os << std::endl;
+    }
+      
+}
+
+ //===========================================================================
+void RevEng::readGrownRegions(istream& is)
+//===========================================================================
+{
+  readClassified(is);
+  int nmb = tri_sf_->size();
+  vector<ftSamplePoint*> tmp_pts(nmb);
+  for (int ka=0; ka<nmb; ++ka)
+    tmp_pts[ka] = (*tri_sf_)[ka];
+  std::set<ftSamplePoint*> tmp_set(tmp_pts.begin(), tmp_pts.end());
+  std::cout << "Read grown, size1 = " << tmp_pts.size() << ", size2 = " << tmp_set.size() << std::endl;
+  curvatureFilter();
+  int num_sfs;
+  is >> num_sfs;
+  if (num_sfs > 0)
+    surfaces_.resize(num_sfs);
+  for (int ki=0; ki<num_sfs; ++ki)
+    {
+      surfaces_[ki] = shared_ptr<HedgeSurface>(new HedgeSurface());
+      surfaces_[ki]->read(is);
+    }
+  
+  int num_regions;
+  is >> num_regions;
+  regions_.resize(num_regions);
+  for (int ki=0; ki<num_regions; ++ki)
+    {
+      vector<int> sf_id;
+      regions_[ki] = shared_ptr<RevEngRegion>(new RevEngRegion(edge_class_type_));
+      regions_[ki]->read(is, tri_sf_, sf_id);
+      for (size_t kj=0; kj<sf_id.size(); ++kj)
+	{
+	  for (size_t kr=0; kr<surfaces_.size(); ++kr)
+	    {
+	      if (sf_id[kj] == surfaces_[kr]->getId())
+		{
+		  regions_[ki]->addHedge(surfaces_[kr].get());
+		  surfaces_[kr]->addRegion(regions_[ki].get());
+		  break;
+		}
+	    }
+	}
+    }
+
+  for (int ki=0; ki<num_regions; ++ki)
+    {
+      regions_[ki]->setRegionAdjacency();
+    }
+
+  int num_single;
+  is >> num_single;
+  single_points_.resize(num_single);
+  for (int ki=0; ki<num_single; ++ki)
+    {
+      int ix;
+      is >> ix;
+      RevEngPoint* pt = dynamic_cast<RevEngPoint*>((*tri_sf_)[ix]);
+      single_points_[ki] = pt;
+    }
+  
+  int num_edgs;
+  is >> num_edgs;
+  if (num_edgs > 0)
+    edges_.resize(num_edgs);
+  for (int ki=0; ki<num_edgs; ++ki)
+    {
+      edges_[ki] = shared_ptr<RevEngEdge>(new RevEngEdge());
+      int id1, id2, id3;
+      vector<int> blend_id;
+      edges_[ki]->read(is, id1, id2, id3, blend_id);
+
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  int id = regions_[kr]->getId();
+	  if (id == id1)
+	    {
+	      edges_[ki]->setReg1(regions_[kr].get());
+	      regions_[kr]->addRevEdge(edges_[ki].get());
+	      break;
+	    }
+	}
+      
+      for (size_t kr=0; kr<regions_.size(); ++kr)
+	{
+	  int id = regions_[kr]->getId();
+	  if (id == id2)
+	    {
+	      edges_[ki]->setReg2(regions_[kr].get());
+	      regions_[kr]->addRevEdge(edges_[ki].get());
+	      break;
+	    }
+	}
+
+      if (id3 >= 0)
+	{
+	  for (size_t kr=0; kr<regions_.size(); ++kr)
+	    {
+	      int id = regions_[kr]->getId();
+	      if (id == id3)
+		{
+		  edges_[ki]->setBlendRegSurf(regions_[kr].get());
+		  regions_[kr]->setBlendEdge(edges_[ki].get());
+		  break;
+		}
+	    }
+	}
+
+      for (size_t kh=0; kh<blend_id.size(); ++kh)
+	{
+	  for (size_t kr=0; kr<regions_.size(); ++kr)
+	    {
+	      int id = regions_[kr]->getId();
+	      if (blend_id[kh] == id)
+		{
+		  edges_[ki]->addBlendRegion(regions_[kr].get());
+		  regions_[kr]->setAssociatedBlend(edges_[ki].get());
+		  break;
+		}
+	    }
+	}
+    }
+
+  int num_axis;
+  is >> num_axis;
+  for (int ki=0; ki<num_axis; ++ki)
+    {
+      Point axis(3);
+      int num_planar, num_rotational;
+      is >> axis[0] >> axis[1] >> axis[2]  >> num_planar >> num_rotational;
+      model_axis_.push_back(AxisInfo(axis));
+      for (int kj=0; kj<num_planar; ++kj)
+	{
+	  Point loc(3);
+	  int num;
+	  is >> loc[0] >> loc[1] >> loc[2] >> num;
+	  model_axis_[ki].addPlaneLocation(loc, num);
+	}
+      for (int kj=0; kj<num_rotational; ++kj)
+	{
+	  Point loc(3);
+	  int num;
+	  is >> loc[0] >> loc[1] >> loc[2] >> num;
+	  model_axis_[ki].addRotationalLocation(loc, num);
+	}
+    }
+  int stop_break = 1;
+}
+  
+
+ //===========================================================================
+void RevEng::storeParams(ostream& os) const
+//===========================================================================
+{
+  os <<  model_character_ << " " << mean_edge_len_ << " " << min_next_;
+  os << " " << norm_ang_lim_;
+  os << " " << norm_plane_lim_ << " " << zero_H_ << " " << zero_K_;
+  os << " " << min_point_region_ << " " << approx_tol_ ;
+  os << " " << anglim_ << " " << max_nmb_outlier_ << " ";
+  os << edge_class_type_ << " " << classification_type_ << std::endl;
+  os << mainaxis_[0] << " " << mainaxis_[1] << " " << mainaxis_[2] << std::endl;
+}
+
+ //===========================================================================
+void RevEng::readParams(istream& is)
+//===========================================================================
+{
+  is >>  model_character_ >> mean_edge_len_ >> min_next_;
+  is >> norm_ang_lim_ >> norm_plane_lim_ >> zero_H_ >> zero_K_;
+  is >> min_point_region_ >> approx_tol_ >> anglim_ >> max_nmb_outlier_;
+  is >> edge_class_type_ >> classification_type_;
+  mainaxis_[0].resize(3);
+  mainaxis_[1].resize(3);
+  mainaxis_[2].resize(3);
+  is >> mainaxis_[0] >> mainaxis_[1] >> mainaxis_[2];
+}
+
+//===========================================================================
+void RevEng::writeRegionWithSurf(ostream& of) const
+//===========================================================================
+{
+  for (size_t kr=0; kr<surfaces_.size(); ++kr)
+    {
+      int numreg = surfaces_[kr]->numRegions();
+      if (numreg > 0)
+	{
+	  RevEngRegion *reg = surfaces_[kr]->getRegion(0);
+	  reg->writeRegionPoints(of);
+	  reg->writeSurface(of);
+	}
+      else
+	{
+	  double diag = bbox_.low().dist(bbox_.high());
+	  surfaces_[kr]->limitSurf(diag);
+	  surfaces_[kr]->surface()->writeStandardHeader(of);
+	  surfaces_[kr]->surface()->write(of);
+	}
+    }
+}
+  
+ //===========================================================================
+void RevEng::writeRegionStage(ostream& of, ostream& ofm, ostream& ofs) const
+//===========================================================================
+{
+  std::cout << "Num regions: " << regions_.size() << ", num surfaces: " << surfaces_.size() << " num edges: " << edges_.size() << std::endl;
+  
+  vector<Vector3D> small;
+  int nmb_one = 0;
+  int low = min_point_region_/10;
+  for (size_t kr=0; kr<regions_.size(); ++kr)
+    {
+      // BoundingBox bbox = regions_[kr]->boundingBox();
+      // if (bbox.low().dist(bbox.high()) < 0.1)
+      //   std::cout << "Small bounding box" << std::endl;
+      // std::set<RevEngPoint*> tmpset(regions_[kr]->pointsBegin(), regions_[kr]->pointsEnd());
+      // if (tmpset.size() != regions_[kr]->numPoints())
+      // 	std::cout << "Point number mismatch. " << kr << " " << tmpset.size() << " " << regions_[kr]->numPoints() << std::endl;
+      int nmb = regions_[kr]->numPoints();
+      if (nmb < low && regions_[kr]->hasSurface() == false)
+	{
+	  for (int ki=0; ki<nmb; ++ki)
+	    small.push_back(regions_[kr]->getPoint(ki)->getPoint());
+	}
+      else if (nmb < min_point_region_ && regions_[kr]->hasSurface() == false)
+	{
+	  ofm << "400 1 0 0" << std::endl;
+	  ofm << nmb << std::endl;
+	  for (int ki=0; ki<nmb; ++ki)
+	    {
+	      ofm << regions_[kr]->getPoint(ki)->getPoint() << std::endl;
+	    }
+	  if (regions_[kr]->hasSurface())
+	    regions_[kr]->writeSurface(ofm);
+	}
+      else
+	{
+	  if (nmb > 0)
+	    {
+	      of << "400 1 0 0" << std::endl;
+	      of << nmb << std::endl;
+	      for (int ki=0; ki<nmb; ++ki)
+		{
+		  of << regions_[kr]->getPoint(ki)->getPoint() << std::endl;
+		}
+	    }
+	  if (regions_[kr]->hasSurface())
+	    regions_[kr]->writeSurface(of);
+	}
+      if (nmb == 1)
+	nmb_one++;
+    }
+  std::cout << "Number of regions with one point: " << nmb_one << std::endl;
+  ofs << "400 1 0 4 0 0 0 255" << std::endl;
+  ofs << small.size() << std::endl;
+  for (size_t kr=0; kr<small.size(); ++kr)
+    ofs << small[kr] << std::endl;
+}
+
+//===========================================================================
+void RevEng::writeEdgeStage(ostream& of) const
+//===========================================================================
+{
+  for (size_t kr=0; kr<edges_.size(); ++kr)
+    {
+      vector<shared_ptr<ParamCurve> > cvs = edges_[kr]->getSpaceCurves();
+      for (size_t kh=0; kh<cvs.size(); ++kh)
+	{
+	  cvs[kh]->writeStandardHeader(of);
+	  cvs[kh]->write(of);
+	}
+      int num_blend = edges_[kr]->numBlendRegs();
+      for (int ka=0; ka<num_blend; ++ka)
+	edges_[kr]->getBlendReg(ka)->writeRegionPoints(of);
+    }
+}
diff --git a/compositemodel/src/RevEngEdge.C b/compositemodel/src/RevEngEdge.C
new file mode 100644
index 00000000..7a550f75
--- /dev/null
+++ b/compositemodel/src/RevEngEdge.C
@@ -0,0 +1,1626 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#include "GoTools/compositemodel/RevEngEdge.h"
+#include "GoTools/compositemodel/HedgeSurface.h"
+#include "GoTools/compositemodel/ftEdge.h"
+#include "GoTools/geometry/Factory.h"
+#include "GoTools/geometry/GoTools.h"
+#include "GoTools/geometry/ObjectHeader.h"
+#include "GoTools/geometry/SurfaceTools.h"
+#include "GoTools/geometry/ClosestPoint.h"
+#include "GoTools/geometry/BoundedUtils.h"
+#include "GoTools/intersections/Identity.h"
+#include <fstream>
+
+//#define DEBUG
+//#define DEBUG_BLEND
+
+using namespace Go;
+using std::vector;
+using std::istream;
+using std::ostream;
+
+//===========================================================================
+RevEngEdge::RevEngEdge()
+//===========================================================================
+  : adjacent1_(0), adjacent2_(0), defined_blend_(0),
+    blend_type_(BLEND_NOT_SET), outer1_(false), outer2_(false),
+    distance_(0.0), radius_(0.0), surfchangecount_(0), extendcount_(0)
+{
+}
+
+//===========================================================================
+RevEngEdge::RevEngEdge(RevEngRegion* reg1, RevEngRegion* reg2)
+//===========================================================================
+  : adjacent1_(reg1), adjacent2_(reg2), defined_blend_(0),
+    blend_type_(BLEND_NOT_SET), outer1_(false), outer2_(false),
+    distance_(0.0), radius_(0.0), surfchangecount_(0), extendcount_(0)
+{
+}
+
+//===========================================================================
+RevEngEdge::RevEngEdge(int type, RevEngRegion* reg1, 
+		       vector<shared_ptr<CurveOnSurface> > cvs1,
+		       bool out1, RevEngRegion* reg2,
+		       vector<shared_ptr<CurveOnSurface> > cvs2,
+		       bool out2, double distance, double radius)
+//===========================================================================
+  : adjacent1_(reg1), adjacent2_(reg2), defined_blend_(0), blend_type_(type),
+    outer1_(out1), outer2_(out2), distance_(distance), radius_(radius),
+    surfchangecount_(0), extendcount_(0)
+{
+  cvs1_.insert(cvs1_.end(), cvs1.begin(), cvs1.end());
+  cvs2_.insert(cvs2_.end(), cvs2.begin(), cvs2.end());
+}
+
+//===========================================================================
+RevEngEdge::~RevEngEdge()
+//===========================================================================
+{
+  if (adjacent1_)
+    adjacent1_->removeRevEngEdge(this);
+  if (adjacent2_)
+    adjacent2_->removeRevEngEdge(this);
+  for (size_t ki=0; ki<blend_regs_.size(); ++ki)
+    blend_regs_[ki]->removeAssociatedBlend();
+  if (defined_blend_)
+    defined_blend_->removeBlendEdge();
+}
+
+
+//===========================================================================
+void RevEngEdge::setReg1(RevEngRegion *reg)
+//===========================================================================
+{
+  adjacent1_ = reg;
+  shared_ptr<ParamSurface> surf;
+  if (reg->hasSurface())
+    surf = reg->getSurface(0)->surface();
+  else if (reg->hasBaseSf())
+    surf = reg->getBase();
+
+  if (surf.get())
+    {
+      for (size_t ki=0; ki<cvs1_.size(); ++ki)
+	cvs1_[ki]->setUnderlyingSurface(surf);
+    }
+}
+
+//===========================================================================
+void RevEngEdge::setReg2(RevEngRegion *reg)
+//===========================================================================
+{
+  adjacent2_ = reg;
+  shared_ptr<ParamSurface> surf;
+  if (reg->hasSurface())
+    surf = reg->getSurface(0)->surface();
+  else if (reg->hasBaseSf())
+    surf = reg->getBase();
+
+  if (surf.get())
+    {
+      for (size_t ki=0; ki<cvs2_.size(); ++ki)
+	cvs2_[ki]->setUnderlyingSurface(surf);
+    }
+}
+
+//===========================================================================
+void RevEngEdge::fixMismatchCurves(double tol)
+//===========================================================================
+{
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    cvs1_[ki]->fixMismatchCurves(tol);
+  for (size_t ki=0; ki<cvs2_.size(); ++ki)
+    cvs2_[ki]->fixMismatchCurves(tol);
+}
+
+//===========================================================================
+void RevEngEdge::replaceSurf(RevEngRegion* reg,
+			     shared_ptr<ParamSurface>& new_surf, double tol)
+//===========================================================================
+{
+  if (reg == adjacent1_)
+    {
+      for (size_t ki=0; ki<cvs1_.size(); ++ki)
+	{
+	  cvs1_[ki]->setUnderlyingSurface(new_surf);
+	  cvs1_[ki]->unsetParameterCurve();
+	  cvs1_[ki]->ensureParCrvExistence(tol);
+#ifdef DEBUG
+	  if (!cvs1_[ki]->hasParameterCurve())
+	    std::cout << "RevEngEdge::replaceSurf: No parameter curve" << std::endl;
+#endif
+	}
+    }
+  else if (reg == adjacent2_)
+    {
+      for (size_t ki=0; ki<cvs2_.size(); ++ki)
+	{
+	  cvs2_[ki]->setUnderlyingSurface(new_surf);
+	  cvs2_[ki]->unsetParameterCurve();
+	  cvs2_[ki]->ensureParCrvExistence(tol);
+#ifdef DEBUG
+	  if (!cvs2_[ki]->hasParameterCurve())
+	    std::cout << "RevEngEdge::replaceSurf: No parameter curve" << std::endl;
+#endif
+	}
+     }
+      
+}
+
+//===========================================================================
+void RevEngEdge::store(ostream& os)
+//===========================================================================
+{
+  os << Id_ << std::endl;
+  int id1 = (adjacent1_) ? adjacent1_->getId() : -1;
+  int id2 = (adjacent2_) ? adjacent2_->getId() : -1;
+  int id3 = (defined_blend_ == 0) ? -1 : defined_blend_->getId();
+  os << id1 << " " << id2 << " " << id3 << std::endl;
+  os << cvs1_.size() << " " << cvs2_.size() << std::endl;
+  
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    {
+      cvs1_[ki]->write(os);
+      // bool space = cvs1_[ki]->hasSpaceCurve();
+      // bool param = cvs1_[ki]->hasParameterCurve();
+      // os << space << " " << param << std::endl;
+      // if (space)
+      // 	{
+      // 	  shared_ptr<ParamCurve> spacecv = cvs1_[ki]->spaceCurve();
+      // 	  shared_ptr<ParamCurve> parcv = cvs1_[ki]->parameterCurve();
+      // 	  spacecv->writeStandardHeader(os);
+      // 	  spacecv->write(os);
+      // 	  parcv->writeStandardHeader(os);
+      // 	  parcv->write(os);
+      // 	}
+    }
+      
+  for (size_t ki=0; ki<cvs2_.size(); ++ki)
+    {
+      cvs2_[ki]->write(os);
+      // bool space = cvs2_[ki]->hasSpaceCurve();
+      // bool param = cvs2_[ki]->hasParameterCurve();
+      // os << space << " " << param << std::endl;
+      // if (space)
+      // 	{
+      // 	  shared_ptr<ParamCurve> spacecv = cvs2_[ki]->spaceCurve();
+      // 	  shared_ptr<ParamCurve> parcv = cvs2_[ki]->parameterCurve();
+      // 	  spacecv->writeStandardHeader(os);
+      // 	  spacecv->write(os);
+      // 	  parcv->writeStandardHeader(os);
+      // 	  parcv->write(os);
+      // 	}
+    }
+
+  os << blend_regs_.size() << std::endl;
+  for (size_t ki=0; ki < blend_regs_.size(); ++ki)
+    os << blend_regs_[ki]->getId() << " ";
+  os << std::endl;
+
+  os << blend_type_ << " " << distance_ << " " << radius_ << " ";
+  os << outer1_ << " " << outer2_ << " ";
+  os << surfchangecount_ << " " << extendcount_ << std::endl;
+}
+
+
+//===========================================================================
+void RevEngEdge::read(istream& is, int& reg_id1, int& reg_id2, int& reg_id3,
+		      vector<int>& blend_id)
+//===========================================================================
+{
+  is >> Id_;
+  is >> reg_id1 >> reg_id2 >> reg_id3;
+  int num_cv1, num_cv2;
+  is >> num_cv1 >> num_cv2;
+  if (num_cv1 > 0)
+    cvs1_.resize(num_cv1);
+  if (num_cv2 > 0)
+    cvs2_.resize(num_cv2);
+  for (int ka=0; ka<num_cv1; ++ka)
+    {
+      cvs1_[ka] = shared_ptr<CurveOnSurface>(new CurveOnSurface());
+      cvs1_[ka]->read(is);
+      // int space, param;
+      // is >> space >> param;
+      // shared_ptr<ParamCurve> spacecv, parcv;
+      // shared_ptr<ParamSurface> dummy;
+      // if (space)
+      // 	{
+      // 	  ObjectHeader header;
+      // 	  header.read(is);
+      // 	  shared_ptr<GeomObject> obj(Factory::createObject(header.classType()));
+      // 	  obj->read(is);
+      // 	  spacecv = dynamic_pointer_cast<ParamCurve,GeomObject>(obj);
+      // 	}
+
+      // if (param)
+      // 	{
+      // 	  ObjectHeader header;
+      // 	  header.read(is);
+      // 	  shared_ptr<GeomObject> obj(Factory::createObject(header.classType()));
+      // 	  obj->read(is);
+      // 	  parcv = dynamic_pointer_cast<ParamCurve,GeomObject>(obj);
+      // 	}
+      
+      // cvs1_[ka] = shared_ptr<CurveOnSurface>(new CurveOnSurface(dummy, parcv,
+      // 								spacecv, false,
+      // 								-1, -1, 0.0,
+      // 								-1, true));
+    }
+
+  for (int ka=0; ka<num_cv2; ++ka)
+    {
+      cvs2_[ka] = shared_ptr<CurveOnSurface>(new CurveOnSurface());
+      cvs2_[ka]->read(is);
+      // int space, param;
+      // is >> space >> param;
+      // shared_ptr<ParamCurve> spacecv, parcv;
+      // shared_ptr<ParamSurface> dummy;
+      // if (space)
+      // 	{
+      // 	  ObjectHeader header;
+      // 	  header.read(is);
+      // 	  shared_ptr<GeomObject> obj(Factory::createObject(header.classType()));
+      // 	  obj->read(is);
+      // 	  spacecv = dynamic_pointer_cast<ParamCurve,GeomObject>(obj);
+      // 	}
+
+      // if (param)
+      // 	{
+      // 	  ObjectHeader header;
+      // 	  header.read(is);
+      // 	  shared_ptr<GeomObject> obj(Factory::createObject(header.classType()));
+      // 	  obj->read(is);
+      // 	  parcv = dynamic_pointer_cast<ParamCurve,GeomObject>(obj);
+      // 	}
+      
+      // cvs2_[ka] = shared_ptr<CurveOnSurface>(new CurveOnSurface(dummy, parcv,
+      // 								spacecv, false,
+      // 								-1, -1, 0.0,
+      // 								-1, true));
+    }
+
+  int num_blend_reg;
+  is >> num_blend_reg;
+  if (num_blend_reg > 0)
+    blend_id.resize(num_blend_reg);
+  for (int ka=0; ka<num_blend_reg; ++ka)
+    is >> blend_id[ka];
+  
+  is >> blend_type_ >> distance_ >> radius_ >> outer1_ >> outer2_;
+  is >> surfchangecount_ >> extendcount_;
+}
+
+
+//===========================================================================
+vector<shared_ptr<ParamCurve> > RevEngEdge::getSpaceCurves()
+//===========================================================================
+{
+  vector<shared_ptr<ParamCurve> > curves;
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    curves.push_back(cvs1_[ki]->spaceCurve());
+
+  return curves;
+}
+
+//===========================================================================
+void RevEngEdge::getCrvEndPoints(Point& pos1, Point& pos2)
+//===========================================================================
+{
+  if (cvs1_.size() > 0)
+    {
+      cvs1_[0]->point(pos1, cvs1_[0]->startparam());
+      cvs1_[cvs1_.size()-1]->point(pos2, cvs1_[cvs1_.size()-1]->endparam());
+    }
+}
+
+//===========================================================================
+void RevEngEdge::closestPoint(const Point& pos, double& par, Point& close,
+			      double& dist)
+//===========================================================================
+{
+  int ix;
+  closestPoint(pos, par, close, dist, ix);
+}
+
+//===========================================================================
+void RevEngEdge::closestPoint(const Point& pos, double& par, Point& close,
+			      double& dist, int& ix)
+//===========================================================================
+{
+  dist = std::numeric_limits<double>::max();
+  ix = -1;
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    {
+      double par1, dist1;
+      Point close1;
+      cvs1_[ki]->closestPoint(pos, cvs1_[ki]->startparam(),
+			      cvs1_[ki]->endparam(), par1, close1, dist1);
+      if (dist1 < dist)
+	{
+	  par = par1;
+	  close = close1;
+	  dist = dist1;
+	  ix = (int)ki;
+	}
+    }
+}
+
+//===========================================================================
+int RevEngEdge::closedSfAtEnd(double tol, double& par, Point& pos, bool at_start)
+//===========================================================================
+{
+  if (cvs1_.size() == 0)
+    return 0;
+  
+  shared_ptr<ParamSurface> surf1 = cvs1_[0]->underlyingSurface();
+  shared_ptr<ParamSurface> surf2 = cvs2_[0]->underlyingSurface();
+
+  // The parameter interval and the space curve of the two curve sequences
+  // correspond
+  int ix = (at_start) ? 0 : (int)cvs1_.size()-1;
+  par = (at_start) ? cvs1_[ix]->startparam() : cvs1_[ix]->endparam();
+  pos = cvs1_[ix]->ParamCurve::point(par);
+
+  double eps = 1.0e-9;
+  double u1, u2, v1, v2, d1, d2;
+  Point close1, close2;
+  surf1->closestBoundaryPoint(pos, u1, v1, close1, d1, eps);
+  surf2->closestBoundaryPoint(pos, u2, v2, close2, d2, eps);
+  if (d1 > tol && d2 > tol)
+    return 0;
+
+  if (d1 <= tol)
+    {
+      RectDomain dom = surf1->containingDomain();
+      if (fabs(u1-dom.umin()) <= eps)
+	{
+	  Point pos2 = surf1->point(dom.umax(), v1);
+	  if (pos.dist(pos2) <= tol)
+	    return 1;
+	}
+      else if (fabs(dom.umax()-u1) <= eps)
+	{
+	  Point pos2 = surf1->point(dom.umin(), v1);
+	  if (pos.dist(pos2) <= tol)
+	    return 1;
+	}
+      if (fabs(v1-dom.vmin()) <= eps)
+	{
+	  Point pos2 = surf1->point(u1, dom.vmax());
+	  if (pos.dist(pos2) <= tol)
+	    return 1;
+	}
+      else if (fabs(dom.vmax()-v1) <= eps)
+	{
+	  Point pos2 = surf1->point(u1, dom.vmin());
+	  if (pos.dist(pos2) <= tol)
+	    return 1;
+	}
+    }
+
+  if (d2 <= tol)
+    {
+      RectDomain dom = surf2->containingDomain();
+      if (fabs(u2-dom.umin()) <= eps)
+	{
+	  Point pos2 = surf2->point(dom.umax(), v2);
+	  if (pos.dist(pos2) <= tol)
+	    return 2;
+	}
+      else if (fabs(dom.umax()-u2) <= eps)
+	{
+	  Point pos2 = surf2->point(dom.umin(), v2);
+	  if (pos.dist(pos2))
+	    return 2;
+	}
+      if (fabs(v2-dom.vmin()) <= eps)
+	{
+	  Point pos2 = surf2->point(u2, dom.vmax());
+	  if (pos.dist(pos2) <= tol)
+	    return 2;
+	}
+      else if (fabs(dom.vmax()-v2) <= eps)
+	{
+	  Point pos2 = surf2->point(u2, dom.vmin());
+	  if (pos.dist(pos2) <= tol)
+	    return 2;
+	}
+    }
+
+  return 0;
+}
+
+//===========================================================================
+bool RevEngEdge::isClosed(double tol)
+//===========================================================================
+{
+  if (cvs1_.size() == 0)
+    return false;
+  double par1 = cvs1_[0]->startparam();
+  double par2 = cvs1_[cvs1_.size()-1]->endparam();
+  Point pos1 = cvs1_[0]->ParamCurve::point(par1);
+  Point pos2 = cvs1_[cvs1_.size()-1]->ParamCurve::point(par2);
+  double dist = pos1.dist(pos2);
+  return (dist <= tol);
+}
+
+//===========================================================================
+bool RevEngEdge::isAdjacent(RevEngEdge* other, double tol, double& par1, double& par2)
+//===========================================================================
+{
+  if (cvs1_.size() == 0)
+    return false;
+  double tpar1 = cvs1_[0]->startparam();
+  double tpar2 = cvs1_[cvs1_.size()-1]->endparam();
+  Point pos1 = cvs1_[0]->ParamCurve::point(tpar1);
+  Point pos2 = cvs1_[cvs1_.size()-1]->ParamCurve::point(tpar2);
+  double tpar3 = other->startparam();
+  double tpar4 = other->endparam();
+  Point pos3 = other->point(tpar3);
+  Point pos4 = other->point(tpar4);
+
+  double dd1 = pos1.dist(pos3);
+  double dd2 = pos1.dist(pos4);
+  double dd3 = pos2.dist(pos3);
+  double dd4 = pos2.dist(pos4);
+  bool adjacent = false;
+  if (dd1 <= tol && dd1 <= std::min(dd2, std::min(dd3,dd4)))
+    {
+      par1 = tpar1;
+      par2 = tpar3;
+      adjacent = true;
+    }
+  else if (dd2 <= tol && dd2 <= std::min(dd3, dd4))
+    {
+      par1 = tpar1;
+      par2 = tpar4;
+      adjacent = true;
+    }
+    else if (dd3 <= tol && dd3 <= dd4)
+    {
+      par1 = tpar2;
+      par2 = tpar3;
+      adjacent = true;
+    }
+    else if (dd4 <= tol)
+     {
+      par1 = tpar2;
+      par2 = tpar4;
+      adjacent = true;
+    }
+    return adjacent;
+}
+
+//===========================================================================
+Point RevEngEdge::point(double par)
+//===========================================================================
+{
+  Point dummy;
+  if (cvs1_.size() == 0)
+    return dummy;
+  
+  size_t ix = 0;
+  for (ix; ix<cvs1_.size() && cvs1_[ix]->endparam() <= par; ++ix);
+  if (ix == cvs1_.size())
+    --ix;
+
+  return cvs1_[ix]->ParamCurve::point(par);
+}
+
+//===========================================================================
+bool RevEngEdge::append(RevEngEdge* other, double tol)
+//===========================================================================
+{
+  double tpar1, tpar2;
+  bool adjacent = isAdjacent(other, tol, tpar1, tpar2);
+  if (!adjacent)
+    return false;   // Cannot append
+  
+  RevEngRegion *adj3, *adj4;
+  other->getAdjacent(adj3, adj4);
+  bool first;
+  if (adjacent1_ == adj3 && adjacent2_ == adj4)
+    first = true;
+  else if (adjacent1_ == adj4 && adjacent2_ == adj3)
+    first = false;
+  else
+    return false;  // Not an appendable configuration
+
+  size_t ncv1 = cvs1_.size();
+  if (ncv1 == 0)
+    return false;
+  
+  size_t ix1 = 0;
+  for (; ix1<ncv1 && cvs1_[ix1]->endparam() <= tpar1; ++ix1);
+  if (ix1 == ncv1)
+    --ix1;
+  Point ppos1 = cvs1_[ix1]->faceParameter(tpar1);
+  Point ppos2 = cvs2_[ix1]->faceParameter(tpar1);
+  
+  vector<shared_ptr<CurveOnSurface> > cvs3, cvs4;
+  other->getCurve(cvs3, true);
+  other->getCurve(cvs4, false);
+  size_t ncv3 = cvs3.size();
+  size_t ix2 = 0;
+  for (; ix2<ncv3 && cvs3[ix2]->endparam() <= tpar2; ++ix2);
+  if (ix2 == ncv3)
+    --ix2;
+  Point ppos3 = cvs3[ix2]->faceParameter(tpar2);
+  Point ppos4 = cvs4[ix2]->faceParameter(tpar2);
+
+  if ((!adjacent1_->hasSurface()) || (!adjacent2_->hasSurface()))
+    return false;  // Unexpected
+  
+  shared_ptr<ParamSurface> surf1 = adjacent1_->getSurface(0)->surface();
+  shared_ptr<ParamSurface> surf2 = adjacent2_->getSurface(0)->surface();
+  Point par_eps1 = SurfaceTools::getParEpsilon(*surf1, tol);
+  Point par_eps2 = SurfaceTools::getParEpsilon(*surf2, tol);
+  double epspar1 = 0.5*(par_eps1[0], par_eps1[1]);
+  double epspar2 = 0.5*(par_eps2[0], par_eps2[1]);
+  double dd1 = ppos1.dist(first ? ppos3 : ppos4);
+  double dd2 = ppos2.dist(first ? ppos4 : ppos3);
+
+  if (dd1 > epspar1 || dd2 > epspar2)
+    return false;
+
+  // Try to append
+  // Copy curves to keep originals in case of failure
+  vector<shared_ptr<CurveOnSurface> > cvs1_2(ncv1);
+  for (size_t ki=0; ki<ncv1; ++ki)
+    cvs1_2[ki] = shared_ptr<CurveOnSurface>(cvs1_[ki]->clone());
+  vector<shared_ptr<CurveOnSurface> > cvs2_2(ncv1);
+  for (size_t ki=0; ki<ncv1; ++ki)
+    cvs2_2[ki] = shared_ptr<CurveOnSurface>(cvs2_[ki]->clone());
+  vector<shared_ptr<CurveOnSurface> > cvs3_2(ncv3);
+  for (size_t ki=0; ki<ncv3; ++ki)
+    cvs3_2[ki] = shared_ptr<CurveOnSurface>(cvs3[ki]->clone());
+  vector<shared_ptr<CurveOnSurface> > cvs4_2(ncv3);
+  for (size_t ki=0; ki<ncv3; ++ki)
+    cvs4_2[ki] = shared_ptr<CurveOnSurface>(cvs4[ki]->clone());
+
+  if (fabs(tpar1-cvs1_2[0]->startparam()) < fabs(cvs1_2[ncv1-1]->endparam()-tpar1))
+    {
+      for (size_t ki=0; ki<ncv1; ++ki)
+	{
+	  cvs1_2[ki]->reverseParameterDirection();
+	  cvs2_2[ki]->reverseParameterDirection();
+	}
+      for (size_t ki=0; ki<ncv1/2; ++ki)
+	{
+	  std::swap(cvs1_2[ki], cvs1_2[ncv1-1-ki]);
+	  std::swap(cvs2_2[ki], cvs2_2[ncv1-1-ki]);
+	}
+    }
+
+  if (fabs(tpar2-cvs3_2[0]->startparam()) > fabs(cvs3_2[ncv3-1]->endparam()-tpar2))
+    {
+      for (size_t ki=0; ki<ncv3; ++ki)
+	{
+	  cvs3_2[ki]->reverseParameterDirection();
+	  cvs4_2[ki]->reverseParameterDirection();
+	}
+      for (size_t ki=0; ki<ncv3/2; ++ki)
+	{
+	  std::swap(cvs3_2[ki], cvs3_2[ncv3-1-ki]);
+	  std::swap(cvs4_2[ki], cvs4_2[ncv3-1-ki]);
+	}
+    }
+
+  // Do append
+  double dist1, dist2;
+  if (first)
+    {
+      cvs1_2[ncv1-1]->appendCurve(cvs3_2[0].get(), 1, dist1, false, epspar1);
+      cvs2_2[ncv1-1]->appendCurve(cvs4_2[0].get(), 1, dist2, false, epspar2);
+    }
+  else
+    {
+      cvs1_2[ncv1-1]->appendCurve(cvs4_2[0].get(), 1, dist1, false, epspar1);
+      cvs2_2[ncv1-1]->appendCurve(cvs2_2[0].get(), 1, dist2, false, epspar2);
+    }
+
+  if (dist1 > tol || dist2 > tol)
+    return false;
+
+  // Check that the parameter curves of the joined curves exists
+  if ((!cvs1_2[ncv1-1]->hasParameterCurve()) ||
+      (!cvs2_2[ncv1-1]->hasParameterCurve()))
+    return false;
+
+  // Replace curves
+  cvs1_.clear();
+  cvs1_.insert(cvs1_.end(), cvs1_2.begin(), cvs1_2.end());
+  if (first && cvs3_2.size() > 1)
+    cvs1_.insert(cvs1_.end(), cvs3_2.begin()+1, cvs3_2.end());
+  else if ((!first) && cvs4_2.size() > 1)
+    cvs1_.insert(cvs1_.end(), cvs4_2.begin()+1, cvs4_2.end());
+
+  cvs2_.clear();
+  cvs2_.insert(cvs2_.end(), cvs2_2.begin(), cvs2_2.end());
+  if (first && cvs4_2.size() > 1)
+    cvs2_.insert(cvs2_.end(), cvs4_2.begin()+1, cvs4_2.end());
+  else if ((!first) && cvs4_2.size() > 1)
+    cvs2_.insert(cvs2_.end(), cvs3_2.begin()+1, cvs3_2.end());
+
+  for (size_t kj=0; kj<other->blend_regs_.size(); ++kj)
+    other->blend_regs_[kj]->setAssociatedBlend(this);
+  blend_regs_.insert(blend_regs_.end(), other->blend_regs_.begin(),
+		     other->blend_regs_.end());
+  other->clearBlendRegions();
+
+  distance_ = 0.5*(distance_ + other->distance_);
+  radius_ = 0.5*(radius_ + other->radius_);
+
+  return true;
+}
+
+//===========================================================================
+int RevEngEdge::missingParCrv()
+//===========================================================================
+{
+  int missing = 0;
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    if (!cvs1_[ki]->hasParameterCurve())
+      {
+	missing += 1;
+	break;
+      }
+  
+  for (size_t ki=0; ki<cvs2_.size(); ++ki)
+    if (!cvs2_[ki]->hasParameterCurve())
+      {
+	missing += 2;
+	break;
+      }
+  return missing;
+}
+
+//===========================================================================
+void RevEngEdge::splitAtSeam(double tol,
+			     vector<shared_ptr<RevEngEdge> >& added_edgs,
+			     vector<shared_ptr<RevEngRegion> >& added_regs,
+			     vector<shared_ptr<HedgeSurface> >& added_sfs)
+//===========================================================================
+{
+  if ((!adjacent1_->hasSurface()) || (!adjacent2_->hasSurface()))
+    return;  // Something is wrong
+#ifdef DEBUG
+  std::ofstream of("edge_split.g2");
+  adjacent1_->writeRegionPoints(of);
+  adjacent2_->writeRegionPoints(of);
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    {
+      shared_ptr<ParamCurve> tmp = cvs1_[ki]->spaceCurve();
+      tmp->writeStandardHeader(of);
+      tmp->write(of);
+    }
+#endif
+  shared_ptr<ParamSurface> surf1 = adjacent1_->getSurface(0)->surface();
+  shared_ptr<ParamSurface> surf2 = adjacent2_->getSurface(0)->surface();
+  shared_ptr<ElementarySurface> elem1 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf1);
+  shared_ptr<ElementarySurface> elem2 =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf2);
+
+  bool close_u1=false, close_u2=false, close_v1=false, close_v2=false;
+  if (elem1.get())
+    elem1->isClosed(close_u1, close_v1);
+  if (elem2.get())
+    elem2->isClosed(close_u2, close_v2);
+
+  RectDomain dom1 = surf1->containingDomain();
+  RectDomain dom2 = surf2->containingDomain();
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    {
+      if (!cvs1_[ki]->hasParameterCurve())
+	{
+	  // Try to split curve
+	  if (close_u1)
+	    {
+	      vector<shared_ptr<ParamCurve> > seam =
+		surf1->constParamCurves(dom1.umin(), false);
+
+	      shared_ptr<ParamCurve> space = cvs1_[ki]->spaceCurve();
+	      double par1, par2, dist;
+	      Point ptc1, ptc2;
+	      ClosestPoint::closestPtCurves(space.get(), seam[0].get(), 
+					    par1, par2, dist, ptc1, ptc2);
+	      if (dist < tol)
+		{
+#ifdef DEBUG
+		  std::ofstream of2("int_with_seam.g2");
+		  space->writeStandardHeader(of2);
+		  space->write(of2);
+		  seam[0]->writeStandardHeader(of2);
+		  seam[0]->write(of2);
+		  of2 << "400 1 0 4 255 0 0 255" << std::endl;
+		  of2 << "1" << std::endl;
+		  of2 << ptc1 << std::endl;
+		  of2 << "400 1 0 4 0 255 0 255" << std::endl;
+		  of2 << "1" << std::endl;
+		  of2 << ptc2 << std::endl;
+#endif
+		  int nr = 1;
+		  shared_ptr<RevEngEdge> new_edge =
+		    doSplit(ki, nr, par1, tol, added_regs, added_sfs);
+		  if (new_edge.get())
+		    {
+		      added_edgs.push_back(new_edge);
+		      new_edge->splitAtSeam(tol, added_edgs, added_regs, added_sfs);
+		    }
+		}
+	    }
+	  
+	  if (close_v1)
+	    {
+	      vector<shared_ptr<ParamCurve> > seam =
+		surf1->constParamCurves(dom1.vmin(), true);
+
+	      shared_ptr<ParamCurve> space = cvs1_[ki]->spaceCurve();
+	      double par1, par2, dist;
+	      Point ptc1, ptc2;
+	      ClosestPoint::closestPtCurves(space.get(), seam[0].get(), 
+					    par1, par2, dist, ptc1, ptc2);
+	      if (dist < tol)
+		{
+#ifdef DEBUG
+		  std::ofstream of2("int_with_seam.g2");
+		  space->writeStandardHeader(of2);
+		  space->write(of2);
+		  seam[0]->writeStandardHeader(of2);
+		  seam[0]->write(of2);
+		  of2 << "400 1 0 4 255 0 0 255" << std::endl;
+		  of2 << "1" << std::endl;
+		  of2 << ptc1 << std::endl;
+		  of2 << "400 1 0 4 0 255 0 255" << std::endl;
+		  of2 << "1" << std::endl;
+		  of2 << ptc2 << std::endl;
+#endif
+		  int nr = 1;
+		  shared_ptr<RevEngEdge> new_edge =
+		    doSplit(ki, nr, par1, tol, added_regs, added_sfs);
+		  if (new_edge.get())
+		    {
+		      added_edgs.push_back(new_edge);
+		      new_edge->splitAtSeam(tol, added_edgs, added_regs, added_sfs);
+		    }
+		}
+	    }
+	}
+    }
+
+  for (size_t ki=0; ki<cvs2_.size(); ++ki)
+    {
+      if (!cvs2_[ki]->hasParameterCurve())
+	{
+	  // Try to split curve
+	  if (close_u2)
+	    {
+	      vector<shared_ptr<ParamCurve> > seam =
+		surf2->constParamCurves(dom2.umin(), false);
+
+	      shared_ptr<ParamCurve> space = cvs2_[ki]->spaceCurve();
+	      double par1, par2, dist;
+	      Point ptc1, ptc2;
+	      ClosestPoint::closestPtCurves(space.get(), seam[0].get(), 
+					    par1, par2, dist, ptc1, ptc2);
+	      if (dist < tol)
+		{
+#ifdef DEBUG
+		  std::ofstream of2("int_with_seam.g2");
+		  space->writeStandardHeader(of2);
+		  space->write(of2);
+		  seam[0]->writeStandardHeader(of2);
+		  seam[0]->write(of2);
+		  of2 << "400 1 0 4 255 0 0 255" << std::endl;
+		  of2 << "1" << std::endl;
+		  of2 << ptc1 << std::endl;
+		  of2 << "400 1 0 4 0 255 0 255" << std::endl;
+		  of2 << "1" << std::endl;
+		  of2 << ptc2 << std::endl;
+#endif
+		  int nr = 2;
+		  shared_ptr<RevEngEdge> new_edge =
+		    doSplit(ki, nr, par1, tol, added_regs, added_sfs);
+		  if (new_edge.get())
+		    {
+		      added_edgs.push_back(new_edge);
+		      new_edge->splitAtSeam(tol, added_edgs, added_regs, added_sfs);
+		    }
+		}
+	    }
+	  
+	  if (close_v1)
+	    {
+	      vector<shared_ptr<ParamCurve> > seam =
+		surf1->constParamCurves(dom1.vmin(), true);
+
+	      shared_ptr<ParamCurve> space = cvs1_[ki]->spaceCurve();
+	      double par1, par2, dist;
+	      Point ptc1, ptc2;
+	      ClosestPoint::closestPtCurves(space.get(), seam[0].get(),
+					    par1, par2, dist, ptc1, ptc2);
+	      if (dist < tol)
+		{
+#ifdef DEBUG
+		  std::ofstream of2("int_with_seam.g2");
+		  space->writeStandardHeader(of2);
+		  space->write(of2);
+		  seam[0]->writeStandardHeader(of2);
+		  seam[0]->write(of2);
+		  of2 << "400 1 0 4 255 0 0 255" << std::endl;
+		  of2 << "1" << std::endl;
+		  of2 << ptc1 << std::endl;
+		  of2 << "400 1 0 4 0 255 0 255" << std::endl;
+		  of2 << "1" << std::endl;
+		  of2 << ptc2 << std::endl;
+#endif
+		  int nr = 2;
+		  shared_ptr<RevEngEdge> new_edge =
+		    doSplit(ki, nr, par1, tol, added_regs, added_sfs);
+		  if (new_edge.get())
+		    {
+		      added_edgs.push_back(new_edge);
+		      new_edge->splitAtSeam(tol, added_edgs, added_regs, added_sfs);
+		    }
+		}
+	    }
+	}
+    }
+
+  
+}
+
+
+//===========================================================================
+shared_ptr<RevEngEdge>
+RevEngEdge::doSplit(size_t ix, int side, double par, double tol,
+		    vector<shared_ptr<RevEngRegion> >& added_regs,
+		    vector<shared_ptr<HedgeSurface> >& added_sfs)
+//===========================================================================
+{
+  double eps = 1.0e-9;
+  shared_ptr<RevEngEdge> new_edg;
+  if (ix >= cvs1_.size() || cvs1_.size() == 0)
+    return new_edg;
+
+  if (par <= cvs1_[ix]->startparam()+eps || par >= cvs1_[ix]->endparam()-eps)
+    return new_edg;
+
+  // Split curves
+  vector<shared_ptr<ParamCurve> > sub1 = cvs1_[ix]->split(par);
+  vector<shared_ptr<ParamCurve> > sub2 = cvs2_[ix]->split(par);
+  if (sub1.size() != 2 || sub2.size() != 2)
+    return new_edg;
+  
+  // Split blend regions
+  vector<RevEngRegion*> move_reg;
+  for (size_t ki=0; ki<blend_regs_.size(); )
+    {
+      vector<RevEngPoint*> points = blend_regs_[ki]->getPoints();
+      vector<RevEngPoint*> keep, move;
+      
+      for (size_t kj=0; kj<points.size(); ++kj)
+	{
+	  Vector3D xyz = points[kj]->getPoint();
+	  Point pnt(xyz[0], xyz[1], xyz[2]);
+	  double par2, dist;
+	  Point close;
+	  int ix2;
+	  closestPoint(pnt, par2, close, dist, ix2);
+	  if (ix2 < ix || par2 <= par)
+	    keep.push_back(points[kj]);
+	  else
+	    move.push_back(points[kj]);
+	}
+
+      if (move.size() == 0)
+	++ki; // Do nothing
+      else if (keep.size() == 0)
+	{
+	  // Move regions to new edge
+	  move_reg.push_back(blend_regs_[ki]);
+	  blend_regs_.erase(blend_regs_.begin()+ki);
+	}
+      else
+	{
+	  // Split region
+	  blend_regs_[ki]->removePoints(move);  // This is not the most effective
+	  // method, but the simplest to implement
+	  blend_regs_[ki]->updateInfo();
+
+	  shared_ptr<RevEngRegion> new_reg(new RevEngRegion(blend_regs_[ki]->getClassificationType(),
+							    blend_regs_[ki]->getEdgeClassificationType(),
+							    move));
+	  added_regs.push_back(new_reg);
+	  move_reg.push_back(new_reg.get());
+	  ++ki;
+	}
+    }
+
+  // Distribute curves
+  vector<shared_ptr<CurveOnSurface> > cvs1_2, cvs2_2;
+  cvs1_2.push_back(dynamic_pointer_cast<CurveOnSurface,ParamCurve>(sub1[1]));
+  cvs1_2[0]->ensureParCrvExistence(tol);
+  for (size_t ki=ix+1; ki<cvs1_.size(); ++ki)
+    cvs1_2.push_back(cvs1_[ki]);
+  cvs2_2.push_back(dynamic_pointer_cast<CurveOnSurface,ParamCurve>(sub2[1]));
+  cvs2_2[0]->ensureParCrvExistence(tol);
+  for (size_t ki=ix+1; ki<cvs2_.size(); ++ki)
+    cvs2_2.push_back(cvs2_[ki]);
+
+  cvs1_[ix] = dynamic_pointer_cast<CurveOnSurface,ParamCurve>(sub1[0]);
+  cvs1_[ix]->ensureParCrvExistence(tol);
+  cvs2_[ix] = dynamic_pointer_cast<CurveOnSurface,ParamCurve>(sub2[0]);
+  cvs2_[ix]->ensureParCrvExistence(tol);
+  if (ix < cvs1_.size()-1)
+    {
+      cvs1_.erase(cvs1_.begin()+ix+1, cvs1_.end());
+      cvs2_.erase(cvs1_.begin()+ix+1, cvs1_.end());
+    }
+
+  if (defined_blend_)
+    {
+      // Split associated surface
+      int stop_blend = 1;
+    }
+
+  new_edg = shared_ptr<RevEngEdge>(new RevEngEdge(blend_type_, adjacent1_,
+						  cvs1_2, outer1_, adjacent2_,
+						  cvs2_2, outer2_, radius_,
+						  distance_));
+  adjacent1_->addRevEdge(new_edg.get());
+  adjacent2_->addRevEdge(new_edg.get());
+  if (move_reg.size() > 0)
+    {
+      for (size_t kr=0; kr<move_reg.size(); ++kr)
+	move_reg[kr]->setAssociatedBlend(new_edg.get());
+      new_edg->addBlendRegions(move_reg);
+    }
+  
+  return new_edg;
+}
+
+//===========================================================================
+bool
+RevEngEdge::updateCurve(double int_tol, double tol, double len)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  if (surfchangecount_ == 0)
+    return false;
+
+  if (cvs1_.size() == 0)
+    return false;
+  
+  shared_ptr<ParamSurface> surf1 = adjacent1_->getSurface(0)->surface();
+  if (!surf1->isBounded())
+    adjacent1_->getSurface(0)->limitSurf(len);
+  shared_ptr<ParamSurface> surf2 = adjacent2_->getSurface(0)->surface();
+  if (!surf2->isBounded())
+    adjacent2_->getSurface(0)->limitSurf(len);
+  shared_ptr<BoundedSurface> bd1, bd2;
+  vector<shared_ptr<CurveOnSurface> > int_cvs1, int_cvs2;
+  BoundedUtils::getSurfaceIntersections(surf1, surf2, int_tol,
+					int_cvs1, bd1,
+					int_cvs2, bd2);
+#ifdef DEBUG_BLEND
+  std::ofstream of_int("intcurves_edge.g2");
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    {
+      shared_ptr<ParamCurve> tmp_cv = cvs1_[ki]->spaceCurve();
+      tmp_cv->writeStandardHeader(of_int);
+      tmp_cv->write(of_int);
+    }
+  for (size_t ki=0; ki<int_cvs1.size(); ++ki)
+    {
+      shared_ptr<ParamCurve> tmp_cv = int_cvs1[ki]->spaceCurve();
+      tmp_cv->writeStandardHeader(of_int);
+      tmp_cv->write(of_int);
+    }
+#endif
+  //  #if 0
+  if (isClosed(tol))
+    replaceCurves(int_cvs1, int_cvs2);  // The seam might have moved
+      
+  else 
+    {
+      if (cvs1_[0]->isClosed())
+	{
+	  cvs1_[0] = int_cvs1[0];
+	  cvs2_[0] = int_cvs2[0];
+	}
+      else
+	{
+	  size_t st = cvs1_.size() - 1;
+	  Point pos1, pos2, pos3, pos4;
+	  double tdel1 = cvs1_[0]->endparam() - cvs1_[0]->startparam();
+	  double tdel2 = cvs1_[st]->endparam() - cvs1_[st]->startparam();
+	  cvs1_[0]->point(pos1, cvs1_[0]->startparam());
+	  cvs1_[0]->point(pos3, cvs1_[0]->startparam() + 0.1*tdel1);
+	  cvs1_[st]->point(pos2, cvs1_[st]->endparam());
+	  cvs1_[st]->point(pos4, cvs1_[st]->endparam() - 0.1*tdel2);
+	  double tp1, tp2;
+	  double td1 = std::numeric_limits<double>::max();
+	  double td2 = std::numeric_limits<double>::max();
+	  double td3 = std::numeric_limits<double>::max();
+	  double td4 = std::numeric_limits<double>::max();
+	  int ix1 = -1, ix2 = -1;
+	  for (size_t kr=0; kr<int_cvs1.size(); ++kr)
+	    {
+	      double tp1_2, tp2_2, tp1_3, tp2_3, td1_2, td2_2, td1_3, td2_3;
+	      Point cl1_2, cl2_2;
+	      double tmin = int_cvs1[kr]->startparam();
+	      double tmax = int_cvs1[kr]->endparam();
+	      int_cvs1[kr]->closestPoint(pos1, tmin, tmax, tp1_2, cl1_2, td1_2);
+	      int_cvs1[kr]->closestPoint(pos2, tmin, tmax, tp2_2, cl2_2, td2_2);
+	      int_cvs1[kr]->closestPoint(pos3, tmin, tmax, tp1_3, cl1_2, td1_3);
+	      int_cvs1[kr]->closestPoint(pos4, tmin, tmax, tp2_3, cl2_2, td2_3);
+	      if (td1_2 < td1-eps || (td1_2 <= td1+eps && td1_3 < td3))
+		{
+		  ix1 = (int)kr;
+		  tp1 = tp1_2;
+		  td1 = td1_2;
+		  td3 = td1_3;
+		}
+	      if (td2_2 < td2-eps || (td2_2 <= td2+eps && td2_3 < td4))
+		{
+		  ix2 = (int)kr;
+		  tp2 = tp2_2;
+		  td2 = td2_2;
+		  td4 = td2_3;
+		}
+	    }
+	  
+	  if (ix2 - ix1 + 1 == (int)cvs1_.size() && tp1 < tp2)
+	    {
+	      for (size_t kr=0; kr<int_cvs1.size(); )
+		{
+		  double tp3 = std::max(tp1, int_cvs1[kr]->startparam());
+		  double tp4 = std::min(tp2, int_cvs1[kr]->endparam());
+		  if (ix1 <= ix2 && ((int)kr < ix1 || (int)kr > ix2))
+		    {
+		      int_cvs1.erase(int_cvs1.begin()+kr);
+		      int_cvs2.erase(int_cvs2.begin()+kr);
+		      if ((int)kr < ix1)
+			ix1--;
+		      if ((int)kr < ix2)
+			ix2--;
+		    }
+		  else if (tp4 > tp3 && tp3 < int_cvs1[kr]->endparam() &&
+			   tp4 > int_cvs1[kr]->startparam() && 
+			   (tp3 > int_cvs1[kr]->startparam() || tp4 < int_cvs1[kr]->endparam()))
+		    {
+		      shared_ptr<CurveOnSurface> sub1(int_cvs1[kr]->subCurve(tp3, tp4));
+		      cvs1_[kr] = sub1;
+		      shared_ptr<CurveOnSurface> sub2(int_cvs2[kr]->subCurve(tp3, tp4));
+		      cvs2_[kr] = sub2;
+#ifdef DEBUG_BLEND
+		      shared_ptr<ParamCurve> tmp_cv = sub1->spaceCurve();
+		      tmp_cv->writeStandardHeader(of_int);
+		      tmp_cv->write(of_int);
+#endif
+		      ++kr;
+		    }
+		  else
+		    {
+		      cvs1_[kr] = int_cvs1[kr];
+		      cvs2_[kr] = int_cvs2[kr];
+		      ++kr;
+		    }
+		}
+	    }
+	}
+    }
+
+  resetSurfChangeCount();
+  return true;
+
+}
+
+//===========================================================================
+void
+RevEngEdge::updateParCurve(RevEngRegion *adj, double int_tol)
+//===========================================================================
+{
+  if (adj == adjacent1_)
+    {
+      for (size_t ki=0; ki<cvs1_.size(); ++ki)
+	cvs1_[ki]->ensureParCrvExistence(int_tol);
+    }
+  else if (adj == adjacent2_)
+    {
+      for (size_t ki=0; ki<cvs2_.size(); ++ki)
+	cvs2_[ki]->ensureParCrvExistence(int_tol);
+    }
+}
+
+//===========================================================================
+bool
+RevEngEdge::extendCurve(double int_tol, double tol, double anglim, 
+			double len, double lenlim, double blendlim,
+			vector<shared_ptr<RevEngRegion> >& added_regions,
+			vector<vector<RevEngPoint*> >& extract_groups,
+			vector<HedgeSurface*>& out_sfs)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  if (extendcount_ == 0)
+    return false;
+
+  if (cvs1_.size() == 0)
+    return false;
+  
+  if (isClosed(tol))
+    {
+      extendcount_ = 0;
+      return false;  // Nothing to do here
+    }
+
+    shared_ptr<ParamSurface> surf1 = adjacent1_->getSurface(0)->surface();
+  if (!surf1->isBounded())
+    adjacent1_->getSurface(0)->limitSurf(len);
+  shared_ptr<ParamSurface> surf2 = adjacent2_->getSurface(0)->surface();
+  if (!surf2->isBounded())
+    adjacent2_->getSurface(0)->limitSurf(len);
+  shared_ptr<BoundedSurface> bd1, bd2;
+  vector<shared_ptr<CurveOnSurface> > int_cvs1, int_cvs2;
+  BoundedUtils::getSurfaceIntersections(surf1, surf2, int_tol,
+					int_cvs1, bd1,
+					int_cvs2, bd2);
+#ifdef DEBUG_BLEND
+  std::ofstream of_int("intcurves_edge.g2");
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    {
+      shared_ptr<ParamCurve> tmp_cv = cvs1_[ki]->spaceCurve();
+      tmp_cv->writeStandardHeader(of_int);
+      tmp_cv->write(of_int);
+    }
+  for (size_t ki=0; ki<int_cvs1.size(); ++ki)
+    {
+      shared_ptr<ParamCurve> tmp_cv = int_cvs1[ki]->spaceCurve();
+      tmp_cv->writeStandardHeader(of_int);
+      tmp_cv->write(of_int);
+    }
+#endif
+
+  if (int_cvs1.size() == 0)
+    return false;
+
+  if (int_cvs1.size() > cvs1_.size())
+    {
+      // Remove possible extra curves
+      vector<Point> mid(cvs1_.size());
+      for (size_t ki=0; ki<cvs1_.size(); ++ki)
+	cvs1_[ki]->point(mid[ki], 0.5*(cvs1_[ki]->startparam()+cvs1_[ki]->endparam()));
+      size_t kr, kh;
+      for (kr=0; kr<int_cvs1.size(); )
+	{
+	  for (kh=0; kh<mid.size(); ++kh)
+	    {
+	      double tpar, dist;
+	      Point close;
+	      int_cvs1[kr]->closestPoint(mid[kh], int_cvs1[kr]->startparam(),
+					 int_cvs1[kr]->endparam(), tpar, close, dist);
+	      if (dist <= tol)
+		break;
+	    }
+	  if (kh == mid.size())
+	    {
+	      int_cvs1.erase(int_cvs1.begin()+kr);
+	      int_cvs2.erase(int_cvs2.begin()+kr);
+	    }
+	  else
+	    ++kr;
+	}
+    }
+
+  // Limit intersection curves to relevant intervals
+  vector<pair<double,double> > t1_t2, t3_t4;
+  bool OK1 =
+    adjacent1_->getCurveRestriction(int_cvs1, tol, anglim, t1_t2);
+  bool OK2 =
+    adjacent2_->getCurveRestriction(int_cvs2, tol, anglim, t3_t4);
+
+  for (int ka=(int)int_cvs1.size()-1; ka>=0; --ka)
+    {
+      double t1 = std::max(t1_t2[ka].first, t3_t4[ka].first);
+      double t2 = std::min(t1_t2[ka].second, t3_t4[ka].second);
+      if (t2 > t1 && (t1 > int_cvs1[ka]->startparam() ||
+		      t2 < int_cvs1[ka]->endparam()))
+	{
+	  double pmin = std::max(t1, int_cvs1[ka]->startparam());
+	  double pmax = std::min(t2, int_cvs1[ka]->endparam());
+	  shared_ptr<CurveOnSurface> sub1(int_cvs1[ka]->subCurve(pmin,pmax));
+	  int_cvs1[ka] = sub1;
+	  shared_ptr<CurveOnSurface> sub2(int_cvs2[ka]->subCurve(pmin,pmax));
+	  int_cvs2[ka] = sub2;
+	}
+
+      if (t2 <= t1 || int_cvs1[ka]->estimatedCurveLength() < lenlim)
+	{
+	  int_cvs1.erase(int_cvs1.begin()+ka);
+	  int_cvs2.erase(int_cvs2.begin()+ka);
+	}
+    }
+  if (int_cvs1.size() == 0)
+    return false;
+  
+  vector<RevEngRegion*> common_reg2 =
+    adjacent1_->commonAdjacent(adjacent2_);
+  for (size_t kj=0; kj<common_reg2.size(); )
+    {
+      // Check if the common region is registered already or is unfeasable for a blend
+      size_t kr=0;
+      for (kr=0; kr<blend_regs_.size(); ++kr)
+	if (common_reg2[kj] == blend_regs_[kr])
+	  break;
+      
+      if (kr < blend_regs_.size() || common_reg2[kj]->hasAssociatedBlend() ||
+	  common_reg2[kj]->hasBlendEdge())
+  	common_reg2.erase(common_reg2.begin()+kj);
+      else
+  	++kj;
+    }
+
+  // Check extension
+  vector<RevEngPoint*> bd_pts1 = adjacent1_->extractBdPoints(); 
+  vector<RevEngPoint*> bd_pts2 = adjacent2_->extractBdPoints();
+  if (bd_pts1.size() == 0 || bd_pts2.size() == 0)
+    return false;
+
+  int num_in_lim1=0, num_in_lim2=0;
+  vector<pair<double, double> > t5_t6, t7_t8;
+  vector<double> wwd1, wwd2;
+  adjacent1_->estimateBlendDimensions(int_cvs1, bd_pts1, tol, blendlim,
+				      t5_t6, wwd1, num_in_lim1);
+
+  adjacent2_->estimateBlendDimensions(int_cvs2, bd_pts2, tol, blendlim,
+				      t7_t8, wwd2, num_in_lim2);
+  if (num_in_lim1 == 0 || num_in_lim2 == 0)
+    return false;
+  if (t5_t6.size() == 0 || t7_t8.size() == 0)
+    return false;
+
+  Point midp = point(0.5*(startparam() + endparam()));
+
+  int ix1 = -1, ix2 = -1;
+  double td1 = std::numeric_limits<double>::max();
+  double td2 = std::numeric_limits<double>::max();
+  for (size_t kj=0; kj<t5_t6.size(); ++kj)
+    {
+      double tmid = 0.5*(t5_t6[kj].first + t5_t6[kj].second);
+      size_t kr;
+      for (kr=0; kr<int_cvs1.size(); ++kr)
+	if (int_cvs1[kr]->startparam()-eps <= tmid && int_cvs1[kr]->endparam()+eps >= tmid)
+	  break;
+      if (kr == int_cvs1.size())
+	return false;  // Should not happen
+
+      double tpar, tdist;
+      Point close;
+      int_cvs1[kr]->closestPoint(midp, t5_t6[kj].first, t5_t6[kj].second,
+				 tpar, close, tdist);
+      if (tdist < td1)
+	{
+	  ix1 = (int)kj;
+	  td1 = tdist;
+	}
+    }
+  
+  for (size_t kj=0; kj<t7_t8.size(); ++kj)
+    {
+      double tmid = 0.5*(t7_t8[kj].first + t7_t8[kj].second);
+      size_t kr;
+      for (kr=0; kr<int_cvs2.size(); ++kr)
+	if (int_cvs2[kr]->startparam()-eps <= tmid && int_cvs2[kr]->endparam()+eps >= tmid)
+	  break;
+      if (kr == int_cvs1.size())
+	return false;  // Should not happen
+
+      double tpar, tdist;
+      Point close;
+      int_cvs2[kr]->closestPoint(midp, t7_t8[kj].first, t7_t8[kj].second,
+				 tpar, close, tdist);
+      if (tdist < td2)
+	{
+	  ix2 = (int)kj;
+	  td2 = tdist;
+	}
+    }
+
+  double t1 = std::max(t5_t6[ix1].first, t7_t8[ix2].first);
+  double t2 = std::min(t5_t6[ix1].second, t7_t8[ix2].second);
+  if (t2 <= t1+eps)
+    return false;  // Nothing left
+  
+  for (int ka=(int)int_cvs1.size()-1; ka>=0; --ka)
+    {
+      if (t2 > t1 && (t1 > int_cvs1[ka]->startparam() ||
+		      t2 < int_cvs1[ka]->endparam()))
+	{
+	  double pmin = std::max(t1, int_cvs1[ka]->startparam());
+	  double pmax = std::min(t2, int_cvs1[ka]->endparam());
+	  shared_ptr<CurveOnSurface> sub1(int_cvs1[ka]->subCurve(pmin,pmax));
+	  int_cvs1[ka] = sub1;
+	  shared_ptr<CurveOnSurface> sub2(int_cvs2[ka]->subCurve(pmin,pmax));
+	  int_cvs2[ka] = sub2;
+	}
+
+      if (t2 <= t1 || int_cvs1[ka]->estimatedCurveLength() < lenlim)
+	{
+	  int_cvs1.erase(int_cvs1.begin()+ka);
+	  int_cvs2.erase(int_cvs2.begin()+ka);
+	}
+    }
+  if (int_cvs1.size() == 0)
+    return false;
+
+  // Adjust added common regions
+  vector<RevEngRegion*> adj_regs;
+  if (common_reg2.size() > 0)
+    {
+      int state = (surf1->instanceType() == Class_Plane ||
+		   surf2->instanceType() == Class_Plane) ? 1 : 2;
+      double angtol = 5.0*anglim;
+      double tol10 = 10.0*tol;
+      vector<RevEngPoint*> near_pts;
+      for (size_t kr=0; kr<common_reg2.size(); ++kr)
+	{
+	  vector<RevEngPoint*> adj_near1, adj_near2;
+	  double dummy_min = 0.0;
+	  for (size_t kh=0; kh<int_cvs1.size(); ++kh)
+	    {
+	      double tmin3 = int_cvs1[kh]->startparam();
+	      double tmax3 = int_cvs1[kh]->endparam();
+	      common_reg2[kr]->getNearPoints(int_cvs1[kh], tmin3, tmax3, distance_,
+					     angtol, adj_near1);
+	      if (state == 1)
+		{
+		  adj_near2 =
+		    adjacent1_->removeOutOfSurf(adj_near1, tol10,
+						angtol, outer1_, dummy_min);
+		  near_pts = 
+		    adjacent2_->removeOutOfSurf(adj_near2, tol10,
+						angtol, outer2_, dummy_min);
+		}
+	      else
+		near_pts = adj_near1;
+	    }
+
+	  if ((int)near_pts.size() == common_reg2[kr]->numPoints())
+	    adj_regs.push_back(common_reg2[kr]);
+	  else if ((int)near_pts.size() < common_reg2[kr]->numPoints())
+	    {
+	      int num_init = common_reg2[kr]->numPoints();
+	      vector<vector<RevEngPoint*> > out_groups;
+	      vector<HedgeSurface*> out_sfs;
+	      vector<vector<RevEngPoint*> > near_groups;
+	      common_reg2[kr]->extractSpesPoints(near_pts, near_groups);
+	      common_reg2[kr]->updateInfo();
+	      common_reg2[kr]->splitRegion(extract_groups);
+	      if (common_reg2[kr]->hasSurface() &&
+		  common_reg2[kr]->numPoints() < num_init/2)
+		{
+		  int num_sf = common_reg2[kr]->numSurface();
+		  for (int ka=0; ka<num_sf; ++ka)
+		    out_sfs.push_back(common_reg2[kr]->getSurface(ka));
+		  common_reg2[kr]->clearSurface();
+		}
+
+	      // Make new region
+	      shared_ptr<RevEngRegion> curr_adj(new RevEngRegion(common_reg2[kr]->getClassificationType(),
+								 common_reg2[kr]->getEdgeClassificationType(),
+								 near_pts));
+	      added_regions.push_back(curr_adj);
+	      adj_regs.push_back(curr_adj.get());
+	    }
+	      
+	}
+    }
+
+  // Update current edge
+  replaceCurves(int_cvs1, int_cvs2);
+  if (adj_regs.size() > 0)
+    addBlendRegions(adj_regs);
+  for (size_t kr=0; kr<adj_regs.size(); ++kr)
+    adj_regs[kr]->setAssociatedBlend(this);
+  extendcount_ = 0;
+  
+  return true;
+}
+  
+
+//===========================================================================
+void
+RevEngEdge::setTrimCurves(double tol, double angtol,
+			  vector<RevEngRegion*>& out_regs,
+			  vector<HedgeSurface*>& out_sfs)
+//===========================================================================
+{
+  if (blend_type_ != NOT_BLEND)
+    return;
+
+  // Ensure parameter curve existence
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    if (!cvs1_[ki]->hasParameterCurve())
+      cvs1_[ki]->ensureParCrvExistence(tol);
+  
+  for (size_t ki=0; ki<cvs2_.size(); ++ki)
+    if (!cvs2_[ki]->hasParameterCurve())
+      cvs2_[ki]->ensureParCrvExistence(tol);
+
+  // Distribute points along the curve as appropriate
+  // Start with the inbetween points
+#ifdef DEBUG_BLEND
+  std::ofstream of1("init_points.g2");
+  adjacent1_->writeRegionPoints(of1);
+  adjacent2_->writeRegionPoints(of1);
+  for (size_t kj=0; kj<blend_regs_.size(); ++kj)
+    blend_regs_[kj]->writeRegionPoints(of1);
+  for (size_t kj=0; kj<cvs1_.size(); ++kj)
+    {
+      cvs1_[kj]->spaceCurve()->writeStandardHeader(of1);
+      cvs1_[kj]->spaceCurve()->write(of1);
+    }
+#endif
+  for (int ka=(int)blend_regs_.size()-1; ka>=0; --ka)
+    {
+      vector<RevEngPoint*> points = blend_regs_[ka]->getPoints();
+      vector<RevEngPoint*> move1, move2;
+      adjacent1_->sortBlendPoints(points, cvs1_, 0.0, adjacent2_,
+				  move1, move2);
+      blend_regs_[ka]->removePoints(move1);
+      adjacent1_->addPointsToGroup(move1, tol, angtol);
+      adjacent2_->addPointsToGroup(move2, tol, angtol);
+      blend_regs_[ka]->removePoints(move2);
+      if (blend_regs_[ka]->hasSurface())
+	{
+	  int num_sf = blend_regs_[ka]->numSurface();
+	  for (int kb=0; kb<num_sf; ++kb)
+	    out_sfs.push_back(blend_regs_[ka]->getSurface(kb));
+	  blend_regs_[ka]->clearSurface();
+	}
+      
+      if (blend_regs_[ka]->numPoints() == 0)
+	{
+	  blend_regs_[ka]->removeFromAdjacent();
+	  blend_regs_[ka]->clearRegionAdjacency();
+	  out_regs.push_back(blend_regs_[ka]);
+	  blend_regs_.erase(blend_regs_.begin()+ka);
+	}
+      int stop_break0 = 1;
+    }
+
+  vector<RevEngPoint*> move_adj1, move_adj2;
+  bool do_move = false;
+  if (do_move)
+    {
+      adjacent1_->extractOutOfEdge2(cvs1_, tol, angtol, move_adj1);
+      adjacent2_->extractOutOfEdge2(cvs2_, tol, angtol, move_adj2);
+    }
+#ifdef DEBUG_BLEND
+  std::ofstream of2("move_points.g2");
+  adjacent1_->writeRegionPoints(of2);
+  if (move_adj1.size() > 0)
+    {
+      of2 << "400 1 0 4 255 0 0 255" << std::endl;
+      of2 << move_adj1.size() << std::endl;
+      for (size_t kj=0; kj<move_adj1.size(); ++kj)
+	of2 << move_adj1[kj]->getPoint() << std::endl;
+    }
+  adjacent2_->writeRegionPoints(of2);
+  if (move_adj2.size() > 0)
+    {
+      of2 << "400 1 0 4 0 255 0 255" << std::endl;
+      of2 << move_adj2.size() << std::endl;
+      for (size_t kj=0; kj<move_adj2.size(); ++kj)
+	of2 << move_adj2[kj]->getPoint() << std::endl;
+    }
+  for (size_t kj=0; kj<cvs1_.size(); ++kj)
+    {
+      cvs1_[kj]->spaceCurve()->writeStandardHeader(of2);
+      cvs1_[kj]->spaceCurve()->write(of2);
+    }
+#endif
+  if (move_adj1.size() > 0)
+    adjacent2_->addPointsToGroup(move_adj1, tol, angtol);
+  if (move_adj2.size() > 0)
+    adjacent1_->addPointsToGroup(move_adj2, tol, angtol);
+  
+  // Define trim curves
+  int stat = 0;
+  for (size_t ki=0; ki<cvs1_.size(); ++ki)
+    {
+      shared_ptr<ftEdge> edg1(new ftEdge(adjacent1_->getSurface(0),
+					 cvs1_[ki], cvs1_[ki]->startparam(),
+					 cvs1_[ki]->endparam()));
+      shared_ptr<ftEdge> edg2(new ftEdge(adjacent2_->getSurface(0),
+					 cvs2_[ki], cvs2_[ki]->startparam(),
+					 cvs2_[ki]->endparam()));
+      edg2->setReversed(true);
+      edg1->connectTwin(edg2.get(), stat);
+      adjacent1_->addTrimEdge(edg1);
+      adjacent2_->addTrimEdge(edg2);
+    }
+
+  int stop_break = 1;
+}
+
+
+//===========================================================================
+bool
+RevEngEdge::contains(RevEngEdge *other, double tol)
+//===========================================================================
+{
+  if (!((adjacent1_ == other->adjacent1_ && adjacent2_ == other->adjacent2_) ||
+	(adjacent1_ == other->adjacent2_ && adjacent2_ == other->adjacent1_)))
+    return false;  // Not the same adjacent surfaces (groups)
+
+  // Assumes only one curve associated to the edge
+  Identity ident;
+  int stat = ident.identicalCvs(cvs1_[0], other->cvs1_[0], tol);
+  if (stat == 1 || stat == 3)
+    return true;
+  else
+    return false;
+}
+
+//===========================================================================
+bool
+RevEngEdge::integrate(RevEngEdge *other)
+//===========================================================================
+{
+  if (!((adjacent1_ == other->adjacent1_ && adjacent2_ == other->adjacent2_) ||
+	(adjacent1_ == other->adjacent2_ && adjacent2_ == other->adjacent1_)))
+    return false;  // Not the same adjacent surfaces (groups)
+
+  if (defined_blend_ && defined_blend_ != other->defined_blend_)
+    return false;
+
+  for (size_t ki=0; ki<other->blend_regs_.size(); ++ki)
+    other->blend_regs_[ki]->setAssociatedBlend(this);
+
+  other->clearBlendRegions();
+  
+  return true;
+}
diff --git a/compositemodel/src/RevEngPoint.C b/compositemodel/src/RevEngPoint.C
new file mode 100644
index 00000000..9c70961f
--- /dev/null
+++ b/compositemodel/src/RevEngPoint.C
@@ -0,0 +1,788 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#include "GoTools/compositemodel/RevEngPoint.h"
+#include "GoTools/compositemodel/RevEngRegion.h"
+#include "GoTools/utils/Array.h"
+#include "GoTools/utils/Point.h"
+#include <vector>
+
+using namespace Go;
+using std::vector;
+  
+//===========================================================================
+RevEngPoint::RevEngPoint()
+  : ftSamplePoint()
+//===========================================================================
+{
+  int dim = 3;
+  eigen1_ = Point(dim);
+  eigen2_ = Point(dim);
+  eigen3_ = Point(dim);
+  LocFuncnormal_ = Point(dim);
+  kvecmin_ = Point(dim);
+  kvecmax_ = Point(dim);
+  lambda1_ = lambda2_ = lambda3_ = -1.0;
+  kmin_ = kmax_ = 0.0;
+  ptdist_ = avdist_ = 0.0;
+  nmb_eigen_ = nmb_locfunc_ = 0;
+  Point dummy(0.0, 0.0, 0.0);
+  // normalcone_.setFromArray(dummy.begin(), dummy.end(), 3);
+  normalcone_ = DirectionCone(dummy);
+  avedglen_ = -1.0;
+  region_ = 0;
+  visited_ = 0;
+  moved_ = 0;
+  outlier_ = false;
+  mark_ix_ = -1;
+  sfdist_ = -1.0;
+  sfang_ = -1.0;
+  nmb_move_ = 0;
+  meancurv0_ = meancurv_ = gausscurv0_ = gausscurv_ = 0.0;
+  curvedness_ = 0.0;
+  edge_[0] = PCA_EDGE_UNDEF;
+  edge_[1] = C1_EDGE_UNDEF;
+  edge_[2] = C2_EDGE_UNDEF;
+  surf_[0] = PCA_UNDEF;
+  surf_[1] = C1_UNDEF;
+}
+
+//===========================================================================
+RevEngPoint::RevEngPoint(Vector3D xyz, int bnd)
+  : ftSamplePoint(xyz, bnd)
+//===========================================================================
+{
+  int dim = 3;
+  eigen1_ = Point(dim);
+  eigen2_ = Point(dim);
+  eigen3_ = Point(dim);
+  LocFuncnormal_ = Point(dim);
+  kvecmin_ = Point(dim);
+  kvecmax_ = Point(dim);
+  lambda1_ = lambda2_ = lambda3_ = -1.0;
+  kmin_ = kmax_ = 0.0;
+  ptdist_ = avdist_ = 0.0;
+  nmb_eigen_ = nmb_locfunc_ = 0;
+  Point dummy(0.0, 0.0, 0.0);
+  // normalcone_.setFromArray(dummy.begin(), dummy.end(), 3);
+  normalcone_ = DirectionCone(dummy);
+  avedglen_ = -1.0;
+  region_ = 0;
+  visited_ = 0;
+  outlier_ = false;
+  mark_ix_ = -1;
+  sfdist_ = -1.0;
+  sfang_ = -1.0;
+  nmb_move_ = 0;
+  meancurv0_ = meancurv_ = gausscurv0_ = gausscurv_ = 0.0;
+  curvedness_ = 0.0;
+  edge_[0] = PCA_EDGE_UNDEF;
+  edge_[1] = C1_EDGE_UNDEF;
+  edge_[2] = C2_EDGE_UNDEF;
+  surf_[0] = PCA_UNDEF;
+  surf_[1] = C1_UNDEF;
+}
+
+//===========================================================================
+RevEngPoint::~RevEngPoint()
+//===========================================================================
+{
+}
+
+//===========================================================================
+double RevEngPoint::getMeanEdgLen()
+//===========================================================================
+{
+  if (avedglen_ < 0.0)
+    {
+      double len = 0.0;
+      for (size_t ki=0; ki<next_.size(); ++ki)
+	{
+	  double currlen = xyz_.dist(next_[ki]->getPoint());
+	  len += currlen;
+	}
+      if (next_.size() > 0)
+	len /= (double)next_.size();
+      avedglen_ = len;
+    }
+  return avedglen_;
+
+}
+
+//===========================================================================
+double RevEngPoint::getMeanEdgLen(double maxlen)
+//===========================================================================
+{
+  int nmb = 0;
+  double len = 0.0;
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      double currlen = xyz_.dist(next_[ki]->getPoint());
+      if (currlen > maxlen)
+	continue;
+	  len += currlen;
+      ++nmb;
+    }
+  if (nmb > 0)
+    len /= (double)nmb;
+  return len;
+
+}
+
+//===========================================================================
+void RevEngPoint::computeTriangNormal(double lim)
+//===========================================================================
+{
+  if (next_.size() == 0)
+    return;
+  double eps = 1.0e-10;
+  size_t prev = next_.size()-1;
+  Vector3D vec1 = next_[prev]->getPoint() - xyz_;
+
+  size_t ki, kj;
+  for (ki=0, kj=0; ki<next_.size(); prev=ki, ++ki)
+    {
+      Vector3D vec2 = next_[ki]->getPoint() - xyz_;
+      Vector3D norm = vec1 % vec2;
+      bool neighbour = next_[ki]->isNeighbour(next_[prev]);
+  
+      if (neighbour && vec1.length() <= lim && vec2.length() <= lim &&
+	  norm.length() > eps)
+	{
+	  if (kj == 0)
+	    normalcone_.setFromArray(norm.begin(), norm.end(), 3);
+	  else
+	    {
+	      Point norm2(norm[0], norm[1], norm[2]);
+	      normalcone_.addUnionWith(norm2);
+	    }
+	  ++kj;
+	}
+      else
+	{
+	  int stop_break = 1;
+	}
+      vec1 = vec2;
+    }
+  if (kj == 0)
+    setOutlier();
+}
+
+//===========================================================================
+int RevEngPoint::surfaceClassification(int classification_type) const
+//===========================================================================
+{
+  if (classification_type == CLASSIFICATION_CURVATURE)
+    return surf_[1];
+  else if (classification_type == CLASSIFICATION_SHAPEINDEX)
+    return surf_[2];
+  else if (classification_type == CLASSIFICATION_POINTASSOCIATION)
+    return surf_[3];
+  else
+    return CLASSIFICATION_UNDEF;
+ }
+
+//===========================================================================
+Point RevEngPoint::fetchClosePoints(double radius, int min_nmb, int max_nmb,
+				    vector<Point>& nearpts)
+//===========================================================================
+{
+  int nmb_iter = 0;
+  int max_iter = 5;
+  // Debug
+  DirectionCone pcone = normalcone_;
+  while ((int)nearpts.size() < min_nmb)
+    {
+      setVisited();
+      vector<RevEngPoint*> near;
+      for (size_t ki=0; ki<next_.size(); ++ki)
+	{
+	  RevEngPoint* curr = dynamic_cast<RevEngPoint*>(next_[ki]);
+	  if (curr->visited())
+	    continue;
+	  if (xyz_.dist(curr->getPoint()) <= radius)
+	    {
+	      curr->setVisited();
+	      near.push_back(curr);
+	      pcone.addUnionWith(curr->normalcone_);
+	      curr->getNearby(xyz_, radius, max_nmb, near);
+	    }
+	}
+      
+      unsetVisited();
+      for (size_t ki=0; ki<near.size(); ++ki)
+	{
+	  Vector3D vx = near[ki]->getPoint();
+	  nearpts.push_back(Point(vx[0], vx[1], vx[2]));
+	  near[ki]->unsetVisited();
+	  pcone.addUnionWith(near[ki]->normalcone_);
+	}
+
+      if (nmb_iter > max_iter)
+	break;
+      
+      if (nearpts.size() < min_nmb)
+	{
+	  radius *= std::max(1.1, (double)min_nmb/(double)nearpts.size());
+	  nearpts.clear();
+	}
+      ++nmb_iter;
+    }
+
+  return Point(xyz_[0], xyz_[1], xyz_[2]);
+}
+
+//===========================================================================
+void RevEngPoint::fetchClosePoints2(double radius, int min_nmb, int max_nmb,
+				    vector<RevEngPoint*>& nearpts,
+				    RevEngRegion *region)
+//===========================================================================
+{
+  int nmb_iter = 0;
+  int max_iter = 5;
+  size_t prev_nmb = nearpts.size();
+  int nmb_same = 0;
+  while ((int)nearpts.size() < min_nmb)
+    {
+      setVisited();
+      vector<RevEngPoint*> near;
+      for (size_t ki=0; ki<next_.size(); ++ki)
+	{
+	  RevEngPoint* curr = dynamic_cast<RevEngPoint*>(next_[ki]);
+	  if (curr->visited())
+	    continue;
+	  if (region && curr->region() != region)
+	    continue;
+	  if (xyz_.dist(curr->getPoint()) <= radius)
+	    {
+	      curr->setVisited();
+	      near.push_back(curr);
+	      curr->getNearby(xyz_, radius, max_nmb, near, region);
+	    }
+	}
+
+      unsetVisited();
+      for (size_t ki=0; ki<near.size(); ++ki)
+	{
+	  nearpts.push_back(near[ki]);
+	  near[ki]->unsetVisited();
+	}
+
+      if (nmb_iter > max_iter)
+	break;
+
+      if (nearpts.size() == prev_nmb)
+	++nmb_same;
+      prev_nmb = nearpts.size();
+      if (nearpts.size() < min_nmb && nmb_iter < max_iter)
+	{
+	  radius *= std::max(1.1, (double)min_nmb/(double)nearpts.size());
+	  nearpts.clear();
+	}
+      ++nmb_iter;
+    }
+  // // Debug
+  // DirectionCone pcone = normalcone_;
+  // for (size_t ki=0; ki<nearpts.size(); ++ki)
+  //   pcone.addUnionWith(nearpts[ki]->normalcone_);
+    if (nearpts.size() < min_nmb && nmb_same > 0)
+      {
+	for (size_t ki=0; ki<nearpts.size(); ++ki)
+	  nearpts[ki]->setOutlier();
+	nearpts.clear();
+    }
+  int stop_break = 1;
+}
+
+//===========================================================================
+void RevEngPoint::fetchConnected(RevEngRegion *region, int max_nmb,
+				 vector<RevEngPoint*>& group)
+//===========================================================================
+{
+  setVisited();
+  group.push_back(this);
+  for (size_t ki=0; ki<group.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next = group[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint* curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	  if (curr->visited())
+	    continue;
+	  if (curr->region() != region)
+	    continue;
+	  curr->setVisited();
+	  group.push_back(curr);
+	}
+      if ((int)group.size() >= max_nmb)
+	break;
+    }
+//   double radius = std::numeric_limits<double>::max();
+
+//   setVisited();
+//   vector<RevEngPoint*> connected;
+//   for (size_t ki=0; ki<next_.size(); ++ki)
+//     {
+//       RevEngPoint* curr = dynamic_cast<RevEngPoint*>(next_[ki]);
+//       if (curr->visited())
+// 	continue;
+//       if (curr->region() != region)
+// 	continue;
+//       curr->setVisited();
+//       connected.push_back(curr);
+//       curr->getNearby(xyz_, radius, max_nmb, connected, region);
+//     }
+//   group.push_back(this);
+//   group.insert(group.end(), connected.begin(), connected.end());
+  int stop_break = 1;
+}
+
+//===========================================================================
+void RevEngPoint::fetchConnectedMarked(int mark,
+				       vector<RevEngPoint*>& group)
+//===========================================================================
+{
+  setVisited();
+  group.push_back(this);
+  for (size_t ki=0; ki<group.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next = group[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint* curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	  if (curr->visited())
+	    continue;
+	  if (curr->getMarkIx() != mark)
+	    continue;
+	  curr->setVisited();
+	  group.push_back(curr);
+	}
+    }
+}
+  
+///===========================================================================
+void RevEngPoint::getNearby(Vector3D xyz, double radius, int max_nmb,
+			    vector<RevEngPoint*>& near, RevEngRegion* region)
+//===========================================================================
+{
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      RevEngPoint* curr = dynamic_cast<RevEngPoint*>(next_[ki]);
+      if (curr->visited())
+	continue;
+      if (region && curr->region() != region)
+	continue;
+      if (xyz.dist(curr->getPoint()) <= radius)
+	{
+	  curr->setVisited();
+	  near.push_back(curr);
+	  if (near.size() < max_nmb)
+	    curr->getNearby(xyz, radius, max_nmb, near, region);
+	}
+    }
+}
+ 
+//===========================================================================
+void
+RevEngPoint::addCovarianceEigen(Point& eigen1, double lambda1, Point& eigen2,
+				double lambda2, Point& eigen3, double lambda3)
+//===========================================================================
+{
+  if (nmb_eigen_ == 0)
+    {
+      eigen1_ = eigen1;
+      eigen2_ = eigen2;
+      eigen3_ = eigen3;
+      lambda1_ = lambda1;
+      lambda2_ = lambda2;
+      lambda3_ = lambda3;
+    }
+  else
+    {
+      eigen1_ = nmb_eigen_*eigen1_ + eigen1;
+      eigen2_ = nmb_eigen_*eigen2_ + eigen2;
+      eigen3_ = nmb_eigen_*eigen3_ + eigen3;
+      lambda1_ = nmb_eigen_*lambda1_ + lambda1;
+      lambda2_ = nmb_eigen_*lambda2_ + lambda2;
+      lambda3_ = nmb_eigen_*lambda3_ + lambda3;
+      eigen1_ /= (double)(nmb_eigen_+1);
+      eigen2_ /= (double)(nmb_eigen_+1);
+      eigen3_ /= (double)(nmb_eigen_+1);
+      lambda1_ /= (double)(nmb_eigen_+1);
+      lambda2_ /= (double)(nmb_eigen_+1);
+      lambda3_ /= (double)(nmb_eigen_+1);
+    }
+  nmb_eigen_++;
+}
+
+//===========================================================================
+void RevEngPoint::addLocFuncInfo(Point& norm, Point& mincvec, double minc, Point& maxcvec,
+			       double maxc, double currdist, double avdist)
+//===========================================================================
+{
+  if (nmb_locfunc_ == 0)
+    {
+      LocFuncnormal_ = norm;
+      kvecmin_ = mincvec;
+      kvecmax_ = maxcvec;
+      kmin_ = minc;
+      kmax_ = maxc;
+      ptdist_ = currdist;
+      avdist_ = avdist;
+    }
+  else
+    {
+      LocFuncnormal_ = nmb_locfunc_*LocFuncnormal_ + norm;
+      kvecmin_ = nmb_locfunc_*kvecmin_ + mincvec;
+      kvecmax_ = nmb_locfunc_*kvecmax_ + maxcvec;
+      kmin_ = nmb_locfunc_*kmin_ + minc;
+      kmax_ = nmb_locfunc_*kmax_ + maxc;
+      ptdist_ = nmb_locfunc_*ptdist_ + currdist;
+      avdist_ = nmb_locfunc_*avdist_ + avdist;
+      LocFuncnormal_ /= (double)(nmb_locfunc_+1);
+      kvecmin_ /= (double)(nmb_locfunc_+1);
+      kvecmax_ /= (double)(nmb_locfunc_+1);
+      kmin_ /= (double)(nmb_locfunc_+1);
+      kmax_ /= (double)(nmb_locfunc_+1);
+      ptdist_ /= (double)(nmb_locfunc_+1);
+      avdist_ /= (double)(nmb_locfunc_+1);
+    }
+  nmb_locfunc_++;
+
+  meancurv0_ = meancurv_ = 0.5*(kmin_ + kmax_);
+  gausscurv0_ = gausscurv_ = kmin_*kmax_;
+  curvedness_ = sqrt(0.5*(kmin_*kmin_ + kmax_*kmax_));
+
+}
+
+
+//===========================================================================
+bool RevEngPoint::isolatedEdge(int edge_class_type, int nmb, bool close)
+//===========================================================================
+{
+  if (notEdge(edge_class_type))
+    return false;
+
+  int nn = 0;
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      RevEngPoint* curr = dynamic_cast<RevEngPoint*>(next_[ki]);
+      bool found = (close) ? curr->closeEdge(edge_class_type) :
+	curr->isEdge(edge_class_type);
+      if (found)
+	++nn;
+    }
+  return (nn <= nmb);
+}
+
+
+//===========================================================================
+void RevEngPoint::adjustWithTriangNorm(double anglim)
+//===========================================================================
+{
+  double ang = getTriangAngle();
+  if (ang < anglim)
+    {
+      if (edge_[0] == PCA_EDGE)
+	edge_[0] = PCA_CLOSE_EDGE;
+     if (edge_[1] == C1_EDGE)
+	edge_[1] = C1_CLOSE_EDGE;
+     if (edge_[2] == C2_EDGE)
+	edge_[2] = C2_CLOSE_EDGE;
+    }
+}
+
+//===========================================================================
+void RevEngPoint::adjacentRegions(vector<RevEngRegion*>& adj) const
+//===========================================================================
+{
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next_[ki]);
+      if (pt->region_ && pt->region_ != region_)
+	{
+	  size_t kj;
+	  for (kj=0; kj<adj.size(); ++kj)
+	    if (pt->region_ == adj[kj])
+	      break;
+	  if (kj == adj.size())
+	    adj.push_back(pt->region_);
+	}
+    }
+}
+
+//===========================================================================
+bool RevEngPoint::nextToRegion(RevEngRegion *reg)
+//===========================================================================
+{
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next_[ki]);
+      if (pt->region_ && pt->region_ == reg)
+	return true;
+    }
+  return false;
+}
+
+
+//===========================================================================
+int RevEngPoint::nmbSameClassification(int classification_type) const
+//===========================================================================
+{
+  int same = 0;
+  int type = surfaceClassification(classification_type);
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next_[ki]);
+      if (pt->surfaceClassification(classification_type) == type)
+	same++;
+    }
+  return same;
+ }
+
+//===========================================================================
+bool RevEngPoint::isNeighbour(RevEngRegion* reg) const
+//===========================================================================
+{
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next_[ki]);
+      if (pt->region() == reg)
+	return true;
+    }
+  return false;
+}
+
+//===========================================================================
+bool RevEngPoint::isNeighbour(RevEngPoint* pt) const
+//===========================================================================
+{
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      if (pt == next_[ki])
+	return true;
+    }
+  return false;
+}
+
+
+
+//===========================================================================
+void RevEngPoint::getAdjInfo(double mean_edge_len, vector<RevEngRegion*>& adj_reg,
+			     vector<RevEngPoint*>& adj_pt)
+//===========================================================================
+{
+  double fac = 10.0;
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      if (pntDist(next_[ki]) > fac*mean_edge_len)
+	  continue;
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next_[ki]);
+      RevEngRegion *curr = pt->region();
+      if (curr)
+	{
+	  size_t kj;
+	  for (kj=0; kj<adj_reg.size(); ++kj)
+	    {
+	      if (adj_reg[kj] == curr)
+		{
+		  if (pntDist(pt) < pntDist(adj_pt[kj]))
+		    adj_pt[kj] = pt;
+		  break;
+		}
+	    }
+	  if (kj == adj_reg.size())
+	    {
+	      adj_reg.push_back(curr);
+	      adj_pt.push_back(pt);
+	    }
+	}
+    }
+}
+
+//===========================================================================
+vector<RevEngRegion*> RevEngPoint::getAdjacentRegions() const
+//===========================================================================
+{
+  vector<RevEngRegion*> adj_reg;
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next_[ki]);
+      RevEngRegion *curr = pt->region();
+      size_t kj;
+      for (kj=0; kj<adj_reg.size(); ++kj)
+	if (adj_reg[kj] == curr)
+	  break;
+      if (kj == adj_reg.size())
+	adj_reg.push_back(curr);
+    }
+		    
+  return adj_reg;
+}
+
+//===========================================================================
+vector<RevEngRegion*> RevEngPoint::adjacentRegsWithSurf() const
+//===========================================================================
+{
+  vector<RevEngRegion*> adj_reg;
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next_[ki]);
+      RevEngRegion *curr = pt->region();
+      if (!curr)
+	continue;
+      if (!curr->hasSurface())
+	continue;
+      size_t kj;
+      for (kj=0; kj<adj_reg.size(); ++kj)
+	if (adj_reg[kj] == curr)
+	  break;
+      if (kj == adj_reg.size())
+	adj_reg.push_back(curr);
+    }
+		    
+  return adj_reg;
+}
+
+//===========================================================================
+int RevEngPoint::numAdjacentRegions() const
+//===========================================================================
+{
+  vector<RevEngRegion*> adj_reg = getAdjacentRegions();
+  return (int)adj_reg.size();
+}
+
+//===========================================================================
+bool RevEngPoint::mergeWithAdjacent(double mean_edge_len)
+//===========================================================================
+{
+  vector<RevEngRegion*> adj_reg;
+  vector<RevEngPoint*> adj_pt;
+  getAdjInfo(mean_edge_len, adj_reg, adj_pt);
+
+  if (adj_reg.size() == 0)
+    return false;
+
+  double lentol = 2.0*mean_edge_len;
+  double angtol = 0.1*M_PI;
+  int minlen = std::numeric_limits<double>::max();
+  int minix = -1;
+  for (size_t ki=0; ki<adj_pt.size(); ++ki)
+    {
+      double len = pntDist(adj_pt[ki]);
+      if (len > lentol)
+	continue;
+      Point monge = adj_pt[ki]->getLocFuncNormal();
+      if (LocFuncnormal_*monge < 0.0 || LocFuncnormal_.angle(monge) > angtol)
+	continue;
+      int ka;
+      for (ka=1; ka<4; ++ka)
+	if (surf_[ka] == adj_pt[ki]->surf_[ka])
+	  break;
+      if (ka == 4)
+	continue;
+      if (len < minlen)
+	{
+	  minlen = len;
+	  minix = (int)ki;
+	}
+    }
+
+  if (minix >= 0)
+    {
+      adj_reg[minix]->addPoint(this);
+      return true;
+    }
+  
+  int stop_break = 1;
+  return false;
+}
+
+//===========================================================================
+void RevEngPoint::store(std::ostream& os) const
+//===========================================================================
+{
+  os << index_ << " " << xyz_ << " " << uv_ << std::endl;
+  os << next_.size();
+  for (size_t ki=0; ki<next_.size(); ++ki)
+    os << " " << next_[ki]->getIndex();
+  os << std::endl;
+  os << avedglen_ << " " << eigen1_ << " " << lambda1_ << " " << eigen2_;
+  os << " " << lambda2_ << " " << eigen3_ << " " << lambda3_ << std::endl;
+  os << LocFuncnormal_ << " " << kvecmin_ << " " << kmin_ << " " << kvecmax_;
+  os << " " << kmax_ << std::endl;
+  os << ptdist_ << " " << avdist_ << std::endl;
+  normalcone_.write(os);
+  for (int ka=0; ka<3; ++ka)
+    os << " " << edge_[ka];
+  for (int ka=0; ka<2; ++ka)
+    os << " " << surf_[ka];
+  os << " " << outlier_ << " " << sfdist_ << " " << sfang_ << std::endl;
+  os << std::endl;
+}
+
+//===========================================================================
+void RevEngPoint::read(std::istream& is, vector<int>& next_ix) 
+//===========================================================================
+{
+  is >> index_ >> xyz_ >> uv_;
+  int nmb_next;
+  is >> nmb_next;
+  if (nmb_next > 0)
+    next_ix.resize(nmb_next);
+  for (int ki=0; ki<nmb_next; ++ki)
+    is >> next_ix[ki];
+  is >> avedglen_ >> eigen1_ >> lambda1_ >> eigen2_ >> lambda2_;
+  is >> eigen3_ >> lambda3_ >> LocFuncnormal_ >> kvecmin_ >> kmin_;
+  is >> kvecmax_ >> kmax_ >> ptdist_ >> avdist_;
+  Point dummy(3);
+  //normalcone_ = DirectionCone(dummy);
+  normalcone_.read(is);
+    meancurv0_ = meancurv_ = 0.5*(kmin_ + kmax_);
+  gausscurv0_ = gausscurv_ = kmin_*kmax_;
+  curvedness_ = sqrt(0.5*(kmin_*kmin_ + kmax_*kmax_));
+  for (int ka=0; ka<3; ++ka)
+    is >> edge_[ka];
+  for (int ka=0; ka<2; ++ka)
+    is >> surf_[ka];
+  is >> outlier_ >> sfdist_ >> sfang_;
+
+}
+
+
diff --git a/compositemodel/src/RevEngRegion.C b/compositemodel/src/RevEngRegion.C
new file mode 100644
index 00000000..e6e2d533
--- /dev/null
+++ b/compositemodel/src/RevEngRegion.C
@@ -0,0 +1,18120 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#include "GoTools/compositemodel/RevEngRegion.h"
+#include "GoTools/compositemodel/RevEngPoint.h"
+#include "GoTools/compositemodel/RevEngUtils.h"
+#include "GoTools/compositemodel/RevEngEdge.h"
+#include "GoTools/compositemodel/RevEng.h"
+#include "GoTools/compositemodel/HedgeSurface.h"
+#include "GoTools/compositemodel/SurfaceModel.h"
+#include "GoTools/compositemodel/Loop.h"
+#include "GoTools/compositemodel/CompositeModelFactory.h"
+#include "GoTools/geometry/Cylinder.h"
+#include "GoTools/geometry/Cone.h"
+#include "GoTools/geometry/Sphere.h"
+#include "GoTools/geometry/Torus.h"
+#include "GoTools/geometry/Circle.h"
+#include "GoTools/geometry/BoundedSurface.h"
+#include "GoTools/geometry/CurveLoop.h"
+#include "GoTools/geometry/LoopUtils.h"
+#include "GoTools/geometry/PointCloud.h"
+//#include "GoTools/utils/Array.h"
+//#include "GoTools/utils/Point.h"
+#include "GoTools/geometry/SplineCurve.h"
+#include "GoTools/geometry/CurveOnSurface.h"
+#include "GoTools/geometry/CurveLoop.h"
+#include "GoTools/geometry/Curvature.h"
+#include "GoTools/geometry/Utils.h"
+#include "GoTools/geometry/SweepSurfaceCreator.h"
+#include "GoTools/geometry/GoIntersections.h"
+#include "GoTools/creators/SmoothCurve.h"
+#include "GoTools/creators/ApproxCurve.h"
+#include "GoTools/creators/ApproxSurf.h"
+#include "GoTools/creators/CurveCreators.h"
+// #include "GoTools/creators/SmoothSurf.h"
+#include "GoTools/geometry/Factory.h"
+#include "GoTools/geometry/GoTools.h"
+#include "GoTools/geometry/ObjectHeader.h"
+#include "GoTools/geometry/ClosestPoint.h"
+#include "GoTools/geometry/GeometryTools.h"
+#include "GoTools/geometry/SplineDebugUtils.h"
+#include "newmat.h"
+#include "newmatap.h"
+#include <vector>
+#include <fstream>
+
+//#define DEBUG_JOIN
+//#define DEBUG_CHECK
+//#define DEBUG_UPDATE
+//#define DEBUG_INTEGRATE
+//#define DEBUG_TORUSCONTEXT
+//#define DEBUG_CYLCONTEXT
+//#define DEBUG
+//#define DEBUG0
+//#define DEBUG_EXTRACT
+//#define DEBUG_CYL
+//#define DEBUG_PLANAR
+//#define DEBUG_SEGMENT
+//#define DEBUG_REPAR
+//#define DEBUG_ADJUST
+//#define DEBUG_GROW
+//#define DEBUG_MERGE
+//#define DEBUG_ADJACENT
+//#define DEBUG_VALIDATE
+//#define DEBUG_COLLECT
+//#define DEBUG_AXIS
+//#define DEBUG_GROWNEIGHBOUR
+//#define DEBUG_TRIM
+//#define DEBUG_BLEND
+
+using namespace Go;
+using std::vector;
+using std::set;
+using std::pair;
+
+//===========================================================================
+RevEngRegion::RevEngRegion(int edge_class_type)
+//===========================================================================
+  : Id_(0), classification_type_(CLASSIFICATION_UNDEF),
+    edge_class_type_(edge_class_type),
+    associated_sf_(0), associated_blend_(0), blend_edge_(0), surfflag_(NOT_SET),
+    surf_adaption_(INITIAL), mink1_(0.0), maxk1_(0.0), 
+    mink2_(0.0), maxk2_(0.0), avH_(0.0), avK_(0.0), MAH_(0.0), MAK_(0.0),
+    frac_norm_in_(0.0), frac_norm_in2_(0.0), maxdist_(0.0), avdist_(0.0), 
+    num_inside_(0), num_inside2_(0), 
+    prev_region_(0), maxdist_base_(0.0), avdist_base_(0.0), num_in_base_(0),
+    visited_(false), to_be_removed_(false)
+{
+  domain_[0] = domain_[1] = domain_[2] = domain_[3] = 0.0;
+  bbox_ = BoundingBox(3);
+}
+
+//===========================================================================
+RevEngRegion::RevEngRegion(int classification_type, int edge_class_type)
+//===========================================================================
+  : Id_(0), classification_type_(classification_type),
+    edge_class_type_(edge_class_type),
+    associated_sf_(0), associated_blend_(0), blend_edge_(0), surfflag_(NOT_SET),
+    surf_adaption_(INITIAL), mink1_(0.0), maxk1_(0.0), 
+    mink2_(0.0), maxk2_(0.0), avH_(0.0), avK_(0.0), MAH_(0.0), MAK_(0.0),
+    frac_norm_in_(0.0), frac_norm_in2_(0.0), maxdist_(0.0), avdist_(0.0), 
+    num_inside_(0), num_inside2_(0), 
+    prev_region_(0), maxdist_base_(0.0), avdist_base_(0.0), num_in_base_(0),
+    visited_(false), to_be_removed_(false)
+{
+  domain_[0] = domain_[1] = domain_[2] = domain_[3] = 0.0;
+  bbox_ = BoundingBox(3);
+}
+
+//===========================================================================
+RevEngRegion::RevEngRegion(int classification_type,
+			   int edge_class_type,
+			   vector<RevEngPoint*> & points)
+//===========================================================================
+  : Id_(0), group_points_(points), classification_type_(classification_type),
+    edge_class_type_(edge_class_type), associated_sf_(0), associated_blend_(0),
+    blend_edge_(0), surfflag_(NOT_SET), surf_adaption_(INITIAL),
+    avH_(0.0), avK_(0.0), MAH_(0.0), MAK_(0.0),
+    frac_norm_in_(0.0), frac_norm_in2_(0.0), maxdist_(0.0), avdist_(0.0), 
+    num_inside_(0), num_inside2_(0), 
+    prev_region_(0), maxdist_base_(0.0), avdist_base_(0.0), num_in_base_(0),
+    visited_(false), to_be_removed_(false)
+{
+  domain_[0] = domain_[1] = domain_[2] = domain_[3] = 0.0;
+
+  for (size_t kj=0; kj<group_points_.size(); ++kj)
+    group_points_[kj]->setRegion(this);
+  
+  // Bounding box and principal curvature summary
+  maxk2_ = std::numeric_limits<double>::lowest();
+  mink2_ = std::numeric_limits<double>::max();
+  maxk1_ = std::numeric_limits<double>::lowest();
+  mink1_ = std::numeric_limits<double>::max();
+  bbox_ = BoundingBox(3);
+  double fac = 1.0/(double)group_points_.size();
+  for  (size_t kj=0; kj<group_points_.size(); ++kj)
+    {
+      double k1 = group_points_[kj]->minPrincipalCurvature();
+      double k2 = group_points_[kj]->maxPrincipalCurvature();
+      double H = group_points_[kj]->meanCurvature();
+      double K = group_points_[kj]->GaussCurvature();
+      mink1_ = std::min(mink1_, fabs(k1));
+      maxk1_ = std::max(maxk1_, fabs(k1));
+      mink2_ = std::min(mink2_, fabs(k2));
+      maxk2_ = std::max(maxk2_, fabs(k2));
+      avH_ += fac*H;
+      avK_ += fac*K;
+      MAH_ += fac*fabs(H);
+      MAK_ += fac*fabs(K);
+      Vector3D point = group_points_[kj]->getPoint();
+      Point point2(point[0], point[1], point[2]);
+      bbox_.addUnionWith(point2);
+    }
+
+  if (group_points_.size() > 0)
+    normalcone_ = DirectionCone(group_points_[0]->getLocFuncNormal());
+  avnorm_ = Point(0.0, 0.0, 0.0);
+  if (group_points_.size() > 0)
+    normalcone2_ = DirectionCone(group_points_[0]->getTriangNormal());
+  avnorm2_ = Point(0.0, 0.0, 0.0);
+  for  (size_t kj=1; kj<group_points_.size(); ++kj)
+    {
+      Point norm = group_points_[kj]->getLocFuncNormal();
+      normalcone_.addUnionWith(norm);
+      avnorm_ += fac*norm;
+      Point norm2 = group_points_[kj]->getTriangNormal();
+      normalcone2_.addUnionWith(norm2);
+      avnorm2_ += fac*norm2;
+    }
+  // (void)avnorm_.normalize_checked();
+  // (void)avnorm2_.normalize_checked();
+}
+
+//===========================================================================
+RevEngRegion::~RevEngRegion()
+//===========================================================================
+{
+  if (associated_blend_ != 0)
+    associated_blend_->removeBlendReg(this);
+  removeFromAdjacent();
+  for (size_t ki=0; ki<rev_edges_.size(); ++ki)
+    {
+      rev_edges_[ki]->eraseAdjacent(this);
+    }
+  for (size_t ki=0; ki<associated_sf_.size(); ++ki)
+    associated_sf_[ki]->removeRegion(this);
+  int stop_break = 1;
+}
+
+//===========================================================================
+void RevEngRegion::setAccuracy(double maxdist, double avdist, int num_inside,
+			       int num_inside2)
+//===========================================================================
+{
+  maxdist_ = maxdist;
+  avdist_ = avdist;
+  num_inside_ = num_inside;
+  num_inside2_ = num_inside2;
+ 
+}
+
+//===========================================================================
+void RevEngRegion::joinToCurrent(double tol, double angtol, int small_lim,
+				vector<RevEngRegion*>& adapted_regions)
+//===========================================================================
+{
+  if (!hasSurface())
+    return;
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  int classtype = surf->instanceType();
+  bool cyllike = (classtype == Class_Cylinder || classtype == Class_Cone);
+  double small_frac = 0.1;
+  double tol_fac = 2.0;
+  
+#ifdef DEBUG_GROW
+  std::ofstream of("master_reg.g2");
+  writeRegionPoints(of);
+  writeSurface(of);
+#endif
+  
+  vector<RevEngRegion*> adj_reg(adjacent_regions_.begin(), adjacent_regions_.end());
+
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    {
+      if (adj_reg[ki]->hasSurface())
+	  continue;  // Not the correct grow method
+      if (adj_reg[ki]->hasAssociatedBlend())
+	continue;
+      
+
+#ifdef DEBUG_GROW
+      std::ofstream ofn("added_region_cand.g2");
+      adj_reg[ki]->writeRegionPoints(ofn);
+#endif
+      
+     vector<RevEngPoint*> points = adj_reg[ki]->getPoints();
+     int num_adj = (int)points.size();
+     int num_curr = (int)group_points_.size();
+
+      // Check if the points can be approximated by the current surface
+       double maxd, avd;
+       int num_in, num2_in;
+       vector<RevEngPoint*> in, out;
+       vector<pair<double, double> > dist_ang;
+       vector<double> parvals;
+       RevEngUtils::distToSurf(points.begin(), points.end(), surf, tol,
+			       maxd, avd, num_in, num2_in,
+			       in, out, parvals, dist_ang, angtol);
+       
+       int num_ang_in = 0;
+       double avang = 0.0;
+       double nfac = 1.0/(double)num_adj;
+       for (size_t kh=0; kh<dist_ang.size(); ++kh)
+	 {
+	   avang += nfac*dist_ang[kh].second;
+	   if (dist_ang[kh].second <= angtol)
+	     num_ang_in++;
+	 }
+       
+       int adj_sf_flag = adj_reg[ki]->defineSfFlag(0, tol, num_in, num2_in, 
+						   avd, cyllike);
+
+       // Combined numbers
+       double maxd2 = std::max(maxdist_, maxd);
+       double avd2 = ((double)num_curr*avdist_ + (double)num_adj*avd)/
+	 (double)(num_curr+num_adj);
+       int num_in2 = num_inside_ + num_in;
+       int num2_in2 = num_inside2_ + num2_in;
+       int sf_flag2 = defineSfFlag(num_curr+num_adj, 0, tol, num2_in2, num_in2,
+				   avd2, cyllike);
+       if (sf_flag2 <= surfflag_ &&
+	   (adj_sf_flag < ACCURACY_POOR ||
+	    (avd <= tol_fac*tol && num_adj < small_lim &&
+	     (double)num_adj < small_frac*(double)num_curr)))
+	 {
+	   vector<RevEngRegion*> added_adjacent;
+	   includeAdjacentRegion(adj_reg[ki], maxd, avd, num_in, 
+				 num2_in, parvals, dist_ang,
+				 added_adjacent);
+	   adapted_regions.push_back(adj_reg[ki]);
+	   setSurfaceFlag(sf_flag2);
+	 }
+    }
+#ifdef DEBUG_GROW
+  std::ofstream of2("updated_region.g2");
+  writeRegionPoints(of2);
+#endif
+  int stop_break = 1;
+}
+ 
+
+
+//===========================================================================
+void RevEngRegion::joinRegions(Point mainaxis[3], double approx_tol, double anglim,
+				vector<RevEngRegion*>& adapted_regions)
+//===========================================================================
+{
+  if (group_points_.size() < 5)
+    return;
+
+#ifdef DEBUG_JOIN
+  std::ofstream of("region_grow.g2");
+  writeRegionInfo(of);
+#endif
+
+  //double eps = 1.0e-6;
+  shared_ptr<ParamSurface> surf1;
+  double avdist1, maxdist1;
+  int num_in1, num2_in1;
+  if (basesf_.get())
+    {
+      surf1 = basesf_;
+      getBaseDist(maxdist1, avdist1, num_in1, num2_in1);
+    }
+  else
+    {
+      surf1 = surfApprox(group_points_, bbox_);
+      vector<RevEngPoint*> in1, out1;
+      approximationAccuracy(group_points_, surf1, approx_tol, anglim, maxdist1, avdist1,
+			    in1, out1);
+      num_in1 = (int)in1.size();
+    }
+#ifdef DEBUG_JOIN
+  std::ofstream of2("approx_sf.g2");
+  surf1->writeStandardHeader(of2);
+  surf1->write(of2);
+#endif
+  
+  if (num_in1 < (int)group_points_.size()/2 || avdist1 > approx_tol)
+    return;
+
+  int kv=0;
+  int numfac = 10;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end();)
+    {
+      ++kv;
+      if ((*it)->hasSurface())
+	{
+	  ++it;
+	  continue;  // Not the correct grow method
+	}
+      
+      if ((*it)->visited())
+	{
+	  ++it;
+	  continue;
+	}
+      (*it)->setVisited(true);
+
+      int num = (int)group_points_.size();
+      int num2 = (*it)->numPoints();
+      if (num2 > numfac*num)
+	{
+	  ++it;
+	  continue;
+	}
+
+      auto itnext = it;
+      ++itnext;
+
+#ifdef DEBUG_JOIN
+      std::ofstream ofn("region_grow_cand.g2");
+      (*it)->writeRegionInfo(ofn);
+#endif
+      
+     vector<RevEngPoint*> points = (*it)->getPoints();
+
+      // Check if the points can be approximated by the current surface
+      double avdist2_0, maxdist2_0;
+      vector<RevEngPoint*> in2_0, out2_0;
+      approximationAccuracy(points, surf1, approx_tol, anglim, maxdist2_0, avdist2_0,
+			    in2_0, out2_0);
+      int num_in2_0 = (int)in2_0.size();
+
+      // Compute overall numbers
+      int tot_num = (int)group_points_.size()+(int)points.size();
+      double frac = (double)(num_in1+num_in2_0)/(double)(tot_num);
+      double avd = (avdist1*(double)group_points_.size() +
+		    avdist2_0*(double)points.size())/(double)tot_num;
+      //if (in2_0.size() > points.size()/2 && avdist2_0 < approx_tol)
+      if (frac > 0.5 && avd < approx_tol)
+	{
+	  // Include region in current
+	  adapted_regions.push_back((*it));
+	  for (auto it2=(*it)->adjacent_regions_.begin();
+	       it2!=(*it)->adjacent_regions_.end(); ++it2)
+	    {
+	      if (*it2 != this)
+		{
+		  addAdjacentRegion(*it2);
+		  (*it2)->addAdjacentRegion(this);
+		  (*it2)->removeAdjacentRegion(*it);
+		}
+	    }
+	  maxdist1 = std::max(maxdist1, maxdist2_0);
+	  avdist1 = (num*avdist1 + num2*avdist2_0)/(double)(num+num2);
+	  for (auto it3=(*it)->pointsBegin(); it3!=(*it)->pointsEnd(); ++it3)
+	    (*it3)->addMove();
+	  vector<pair<double,double> > dummy;
+	  addRegion((*it), dummy);
+	  removeAdjacentRegion(*it);
+	  it = itnext;
+	  continue;
+	}
+			     
+
+      bool always_stop = true;
+      if (num2 < 5 || num2 > num || always_stop)
+	{
+	  it = itnext;
+	  continue;
+	}
+      
+      shared_ptr<ParamSurface> surf2;
+
+      if (basesf_.get() &&
+	  ((!(*it)->basesf_.get()) ||
+	   ((*it)->basesf_.get() &&
+	    basesf_->instanceType() == (*it)->basesf_->instanceType())))
+	{
+	  vector<RevEngPoint*> all_pts;
+	  all_pts.insert(all_pts.end(), group_points_.begin(), group_points_.end());
+	  all_pts.insert(all_pts.end(), points.begin(), points.end());
+	  if (basesf_->instanceType() == Class_Plane)
+	    surf2 = computePlane(all_pts, avnorm_, mainaxis);
+	  else if (basesf_->instanceType() == Class_Cylinder)
+	    {
+	      surf2 = computeCylinder(all_pts, approx_tol);
+	    }
+	}
+      else
+	surf2 = surfApprox(points, (*it)->getBbox());
+
+      if (!surf2.get())
+	{
+	  it = itnext;
+	  continue;
+	}
+	
+#ifdef DEBUG_JOIN
+      std::ofstream of3("approx_sf2.g2");
+      surf2->writeStandardHeader(of3);
+      surf2->write(of3);
+#endif
+      
+      double avdist2, maxdist2;
+      vector<RevEngPoint*> in2, out2;
+      approximationAccuracy(points, surf2, approx_tol, anglim,
+			    maxdist2, avdist2, in2, out2);
+      if (in2.size() < points.size()/2 && maxdist2 > approx_tol)
+	{
+	  it = itnext;
+	  continue;
+	}
+      int num_in2 = (int)in2.size();
+
+      vector<RevEngPoint*> mergept(group_points_.begin(), group_points_.end());
+      mergept.insert(mergept.end(), (*it)->pointsBegin(),
+		     (*it)->pointsEnd());
+      BoundingBox bbox = bbox_;
+      bbox.addUnionWith((*it)->getBbox());
+
+      shared_ptr<SplineSurface> surf3 = surfApprox(mergept, bbox);
+#ifdef DEBUG_JOIN
+      std::ofstream of4("approx_sf3.g2");
+      surf3->writeStandardHeader(of4);
+      surf3->write(of4);
+#endif
+      
+      double avdist3_0, maxdist3_0;
+      vector<RevEngPoint*> in3_0, out3_0;
+      approximationAccuracy(points, surf3, approx_tol, anglim,
+			    maxdist3_0, avdist3_0, in3_0, out3_0);
+      if (in3_0.size() < points.size()/2 || avdist3_0 > approx_tol)
+	{
+	  it = itnext;
+	  continue;
+	}
+
+      
+      double avdist3, maxdist3;
+      vector<RevEngPoint*> in3, out3;
+      approximationAccuracy(mergept, surf3, approx_tol, anglim,
+			    maxdist3, avdist3, in3, out3);
+      int num_in3 = (int)in3.size();
+      
+      if (/*maxdist3 <= 1.1*std::max(maxdist1, maxdist2) && */
+	  avdist3 <= 1.1*std::max(avdist1, avdist2) && avdist3 < approx_tol &&
+	  num_in3 > num_in1 && num_in3 > 3*(num_in1+num_in2)/4)
+	{
+	  // Include region in current
+	  adapted_regions.push_back((*it));
+	  for (auto it2=(*it)->adjacent_regions_.begin();
+	       it2!=(*it)->adjacent_regions_.end(); ++it2)
+	    {
+	      if (*it2 != this)
+		{
+		  addAdjacentRegion(*it2);
+		  (*it2)->addAdjacentRegion(this);
+		  (*it2)->removeAdjacentRegion(*it);
+		}
+	    }
+	  maxdist1 = maxdist3;
+	  avdist1 = avdist3;
+	  for (auto it3=(*it)->pointsBegin(); it3!=(*it)->pointsEnd(); ++it3)
+	    (*it3)->addMove();
+	  vector<pair<double,double> > dummy;
+	  addRegion((*it), dummy);
+	  removeAdjacentRegion(*it);
+	  num_in1 = num_in3;
+	  surf1 = surf3;
+	}
+
+#ifdef DEBUG_JOIN
+      std::ofstream ofl("region_grow2.g2");
+      writeRegionInfo(ofl);
+#endif
+      int stop_break = 1;
+      it = itnext;
+    }
+
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    (*it)->setVisited(false);
+      
+}
+
+  
+//===========================================================================
+void RevEngRegion::approximationAccuracy(vector<RevEngPoint*>& points,
+					 shared_ptr<ParamSurface> surf,
+					 double tol, double angtol,
+					 double& maxd, double& avd,
+					 vector<RevEngPoint*>& in,
+					 vector<RevEngPoint*>& out)
+//===========================================================================
+{
+  avd = maxd = 0.0;
+  double eps = 1.0e-6;
+  double wgt = 1.0/(double)points.size();
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pos(xyz[0], xyz[1], xyz[2]);
+      double upar, vpar, dist;
+      Point close;
+      surf->closestPoint(pos, upar, vpar, close, dist, eps);
+      avd += wgt*dist;
+			       maxd = std::max(maxd, dist);
+      if (dist < tol)
+	{
+	  Point normal;
+	  surf->normal(normal, upar, vpar);
+	  double ang = normal.angle(points[ki]->getLocFuncNormal());
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang < angtol)
+	    in.push_back(points[ki]);
+	  else
+	    out.push_back(points[ki]);
+	}
+      else
+	out.push_back(points[ki]);
+    }
+}
+
+//===========================================================================
+void RevEngRegion::splitPlanar(double lim_cone, int min_point_reg,
+			       vector<vector<RevEngPoint*> >& other_groups,
+			       vector<RevEngPoint*>& single)
+//===========================================================================
+{
+  vector<vector<RevEngPoint*> > groups;
+  vector<DirectionCone> cones;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point norm = group_points_[ki]->getTriangNormal();
+      size_t kj;
+      for (kj=0; kj<cones.size(); ++kj)
+	{
+	  double ang = cones[kj].unionAngle(norm);
+	  if (ang <= lim_cone)
+	    {
+	      cones[kj].addUnionWith(norm);
+	      groups[kj].push_back(group_points_[ki]);
+	      break;
+	    }
+	}
+      if (kj == cones.size())
+	{
+	  DirectionCone curr(norm);
+	  cones.push_back(curr);
+	  vector<RevEngPoint*> other;
+	  other.push_back(group_points_[ki]);
+	  groups.push_back(other);
+	}
+    }
+#ifdef DEBUG_PLANAR
+  std::ofstream of("planar_groups.g2");
+  for (size_t ki=0; ki<groups.size(); ++ki)
+    {
+      of << "400 1 0 4 100 155 0 255" << std::endl;
+      of << groups[ki].size() << std::endl;
+      for (size_t kj=0; kj<groups[ki].size(); ++kj)
+	of << groups[ki][kj]->getPoint() << std::endl;
+    }
+#endif
+  if (groups.size() == 1)
+    return;
+  
+  vector<RevEngPoint*> remain;
+  for (int ka=(int)groups.size()-1; ka>=0; --ka)
+    {
+      if ((int)groups[ka].size() < min_point_reg)
+	{
+	  remain.insert(remain.end(), groups[ka].begin(), groups[ka].end());
+	  groups.erase(groups.begin()+ka);
+	}
+    }
+
+  size_t num_groups = groups.size();
+  for (size_t ki=0; ki<num_groups; ++ki)
+    {
+      vector<vector<RevEngPoint*> > connected;
+      vector<RevEngPoint*> dummy;
+      connectedGroups(groups[ki], connected, false, dummy);
+      for (size_t kj=1; kj<connected.size(); ++kj)
+	if (connected[kj].size() > connected[0].size())
+	  std::swap(connected[kj], connected[0]);
+
+      groups[ki] = connected[0];
+      for (size_t kj=1; kj<connected.size(); ++kj)
+	{
+	  if (connected[kj].size() >= min_point_reg)
+	    groups.push_back(connected[kj]);
+	  else
+	    remain.insert(remain.end(), connected[kj].begin(), connected[kj].end());
+	}
+    }
+
+  for (size_t ki=1; ki<groups.size(); ++ki)
+    if (groups[ki].size() > groups[0].size())
+      std::swap(groups[ki], groups[0]);
+
+  std::swap(group_points_, groups[0]);
+  updateInfo();
+
+  if (groups.size() > 0)
+    other_groups.insert(other_groups.end(), groups.begin()+1, groups.end());
+
+  if (remain.size() > 0)
+    {
+      vector<vector<RevEngPoint*> > connected2;
+      vector<RevEngPoint*> dummy2;
+      connectedGroups(remain, connected2, false, dummy2);
+      for (size_t kj=0; kj<connected2.size(); ++kj)
+	{
+	  if (connected2[kj].size() == 1)
+	    {
+	      connected2[kj][0]->unsetRegion();
+	      single.push_back(connected2[kj][0]);
+	    }
+	  else
+	    other_groups.push_back(connected2[kj]);
+	}
+    }
+  int stop_break = 1;
+}
+
+//===========================================================================
+void RevEngRegion::updateRegion(double approx_tol, double anglim,
+				vector<RevEngRegion*>& adapted_regions,
+				vector<shared_ptr<RevEngRegion> >& outdiv_regions)
+//===========================================================================
+{
+  if (group_points_.size() < 5)
+    return;
+
+#ifdef DEBUG_UPDATE
+  std::ofstream of("region_grow.g2");
+  writeRegionInfo(of);
+#endif
+  double eps = 1.0e-6;
+  shared_ptr<SplineSurface> surf = surfApprox(group_points_, bbox_);
+#ifdef DEBUG_UPDATE
+  std::ofstream of2("approx_sf.g2");
+  surf->writeStandardHeader(of2);
+  surf->write(of2);
+#endif  
+  // Check if the accuracy is sufficient as a basis for growth
+  double avdist = 0.0, maxdist = 0.0;
+  vector<RevEngPoint*> in, out;
+  approximationAccuracy(group_points_, surf, approx_tol, anglim, maxdist, avdist,
+			in, out);
+  if (in.size() < group_points_.size()/2 || avdist > approx_tol)
+    return;
+
+  setBaseSf(surf, maxdist, avdist, (int)in.size(), (int)in.size());
+  
+  // Grow
+  vector<RevEngPoint*> visited;
+  int numpt = (int)group_points_.size();
+  int numfac = 10;
+  // std::set<RevEngPoint*> tmpset0(in.begin(), in.end());
+  // if (tmpset0.size() != in.size())
+  //   std::cout << "Point number mismatch, init. " << tmpset0.size() << " " << in.size() << std::endl;
+  size_t ki = 0;
+  for (int ka=0; ka<10; ++ka)
+    {
+      size_t in_size = in.size();
+      for (; ki<in.size(); ++ki)
+	{
+	  vector<ftSamplePoint*> next = in[ki]->getNeighbours();
+	  for (size_t kj=0; kj<next.size(); ++kj)
+	    {
+	      RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	      if (curr->moved())
+		continue;  // Already in an extended region
+	      if (curr->isEdge(edge_class_type_))
+		continue;  // Do not include points labelled as edge
+	      if (curr->region() == this)
+		continue;
+	      if (curr->visited())
+		continue;
+
+	      // if (std::find(in.begin(), in.end(), curr) != in.end())
+	      // 	{
+	      // 	  std::cout << "Double point" << curr << std::endl;
+	      // 	}
+	      curr->setVisited();
+	      visited.push_back(curr);
+	      int numpt2 = 0;
+	      if (curr->region())
+		numpt2 = curr->region()->numPoints();
+	      if (numpt2 > numfac*numpt)
+		continue;
+	      Vector3D xyz = curr->getPoint();
+	      Point pos(xyz[0], xyz[1], xyz[2]);
+	      double upar, vpar, dist;
+	      Point close;
+	      surf->closestPoint(pos, upar, vpar, close, dist, eps);
+	      if (dist < approx_tol)
+		{
+		  Point normal;
+		  surf->normal(normal, upar, vpar);
+		  double ang = normal.angle(curr->getLocFuncNormal());
+		  ang = std::min(ang, M_PI-ang);
+		  if (ang < anglim)
+		    in.push_back(curr);
+		  else
+		    out.push_back(curr);
+		}
+	      else
+		out.push_back(curr);
+	    }
+	}
+#ifdef DEBUG_UPDATE
+      std::ofstream of3("in_out.g2");
+      of3 << "400 1 0 4 255 0 0 255" << std::endl;
+      of3 << in.size() << std::endl;
+      for (size_t kj=0; kj<in.size(); ++kj)
+	of3 << in[kj]->getPoint() << std::endl;
+      of3 << "400 1 0 4 0 255 0 255" << std::endl;
+      of3 << out.size() << std::endl;
+      for (size_t kj=0; kj<out.size(); ++kj)
+	of3 << out[kj]->getPoint() << std::endl;
+#endif
+      if (in.size() == in_size)
+	break;
+
+      vector<RevEngPoint*> in_out(in.begin(), in.end());
+      in_out.insert(in_out.end(), out.begin(), out.end());
+      shared_ptr<SplineSurface> surf2 = surfApprox(in_out, bbox_);
+#ifdef DEBUG_UPDATE
+      std::ofstream of4("approx_sf2.g2");
+      surf2->writeStandardHeader(of4);
+      surf2->write(of4);
+#endif
+      //int nmb_in2 = 0;
+      double avdist2 = 0.0;
+      double maxdist2 = 0.0;
+      in.clear();
+      out.clear();
+      double wgt = 1.0/(double)in_out.size();
+      for (size_t ki=0; ki<in_out.size(); ++ki)
+	{
+	  Vector3D xyz = in_out[ki]->getPoint();
+	  Point pos(xyz[0], xyz[1], xyz[2]);
+	  double upar, vpar, dist;
+	  Point close;
+	  surf2->closestPoint(pos, upar, vpar, close, dist, eps);
+	  avdist2 += wgt*dist;
+	  maxdist2 = std::max(maxdist2,dist);
+	  if (dist < approx_tol)
+	    {
+	      Point normal;
+	      surf2->normal(normal, upar, vpar);
+	      double ang = normal.angle(in_out[ki]->getLocFuncNormal());
+	      ang = std::min(ang, M_PI-ang);
+	      if (ang < anglim)
+		in.push_back(in_out[ki]);
+	      else
+		out.push_back(in_out[ki]);
+	    }
+	  else
+	    out.push_back(in_out[ki]);
+	}
+
+      if (in.size() < out.size() || avdist2 > approx_tol)
+	{
+	  break;
+	}
+
+      setBaseSf(surf2, maxdist2, avdist2, (int)in.size(), (int)in.size());
+      surf = surf2;
+      // std::set<RevEngPoint*> tmpset(in.begin(), in.end());
+      // if (tmpset.size() != in.size())
+      // 	std::cout << "Point number mismatch. " << ka << " " << tmpset.size() << " " << in.size() << std::endl;
+      int stop_break0 = 1;
+    }
+
+  //std::cout << "Ready to move points " << std::endl;
+  // Move in points
+  set<RevEngRegion*> affected_reg;
+  for (size_t ki=0; ki<in.size(); ++ki)
+    {
+      in[ki]->setMoved();
+      
+      if (in[ki]->region() == this)
+	continue;
+
+      RevEngRegion *other_reg = in[ki]->region();
+      if (other_reg)
+	{
+	  other_reg->removePoint(in[ki]);
+	  affected_reg.insert(other_reg);
+	}
+      in[ki]->setRegion(this);
+      in[ki]->addMove();
+      group_points_.push_back(in[ki]);
+    }
+
+  for (size_t ki=0; ki<visited.size(); ++ki)
+    visited[ki]->unsetVisited();
+      
+  vector<RevEngRegion*> affected_reg2(affected_reg.begin(), affected_reg.end());
+  for (size_t ki=0; ki<affected_reg2.size(); ++ki)
+    {
+      if (affected_reg2[ki]->numPoints() == 0)
+	{
+	  for (auto it=affected_reg2[ki]->adjacent_regions_.begin();
+	       it!=affected_reg2[ki]->adjacent_regions_.end(); ++it)
+	    {
+	      // if ((*it) != this)
+	      //   {
+	      // 	addAdjacentRegion(*it);
+	      (*it)->removeAdjacentRegion(affected_reg2[ki]);
+	      // }
+	    }
+	  // removeAdjacentRegion(affected_reg2[ki]);
+	  adapted_regions.push_back(affected_reg2[ki]);
+	}
+      else
+	{
+	  vector<vector<RevEngPoint*> > separate_groups;
+	  affected_reg2[ki]->splitRegion(separate_groups);
+	  int classtype = affected_reg2[ki]->getClassificationType();
+	  for (size_t ki=0; ki<separate_groups.size(); ++ki)
+	    {
+	      shared_ptr<RevEngRegion> reg(new RevEngRegion(classtype,
+							    edge_class_type_,
+							    separate_groups[ki]));
+	      outdiv_regions.push_back(reg);
+	    }
+	  affected_reg2[ki]->updateInfo(approx_tol, anglim);
+	}
+    }
+
+  updateInfo(approx_tol, anglim);
+  int stop_break = 1;
+  
+}
+
+//===========================================================================
+bool RevEngRegion::segmentByPlaneAxis(Point mainaxis[3], int min_point_in,
+				      int min_pt_reg,
+				      double tol, double angtol, int prefer_elementary,
+				      vector<RevEngRegion*>& adj_planar,
+				      vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				      vector<shared_ptr<RevEngRegion> >& added_reg,
+				      vector<HedgeSurface*>& prevsfs,
+				      vector<vector<RevEngPoint*> >& added_groups)
+//===========================================================================
+{
+#ifdef DEBUG_SEGMENT
+  std::ofstream of("adj_planar.g2");
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    adj_planar[ki]->writeRegionPoints(of);
+#endif
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem(adj_planar.size());
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    {
+      if (!adj_planar[ki]->hasSurface())
+	return false;
+      shared_ptr<ParamSurface> surf = adj_planar[ki]->getSurface(0)->surface();
+      shared_ptr<ElementarySurface> elemsf =
+	dynamic_pointer_cast<Plane,ParamSurface>(surf);
+      if (!elemsf.get())
+	return false;
+      adj_elem[ki] = std::make_pair(elemsf, adj_planar[ki]);
+    }
+
+  // Get candidate axis directions
+  vector<Point> dir;
+  size_t kr;
+  for (size_t ki=0; ki<adj_elem.size(); ++ki)
+    {
+      Point dir1 = adj_elem[ki].first->direction();
+      for (kr=0; kr<dir.size(); ++kr)
+	{
+	  double ang = dir[kr].angle(dir1);
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang <= angtol)
+	    break;
+	}
+      if (kr == dir.size())
+	dir.push_back(dir1);
+      for (size_t kj=ki+1; kj<adj_elem.size(); ++kj)
+	{
+	  Point dir2 = adj_elem[kj].first->direction();
+	  double ang = dir1.angle(dir2);
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang > angtol)
+	    {
+	      Point dir3 = dir1.cross(dir2);
+	      for (kr=0; kr<dir.size(); ++kr)
+		{
+		  ang = dir[kr].angle(dir3);
+		  ang = std::min(ang, M_PI-ang);
+		  if (ang <= angtol)
+		    break;
+		}
+	      if (kr == dir.size())
+		dir.push_back(dir3);
+	    }
+	}
+    }
+
+  vector<vector<RevEngPoint*> > group1, group2;
+  vector<RevEngPoint*> remaining;
+  double axisang = 0.1*M_PI;
+  double planeang = 0.05;
+  bool divided = sortByAxis(dir, tol, axisang, planeang, group1, group2, remaining);
+
+  vector<RevEngPoint*> all;
+  for (size_t ki=0; ki<group1.size(); ++ki)
+    all.insert(all.end(), group1[ki].begin(), group1[ki].end());
+  for (size_t ki=0; ki<group2.size(); ++ki)
+    all.insert(all.end(), group2[ki].begin(), group2[ki].end());
+  all.insert(all.end(), remaining.begin(), remaining.end());
+  std::set<RevEngPoint*> tmpset1(all.begin(), all.end());
+  if (tmpset1.size() != all.size())
+    std::cout << "Point number mismatch,  all. " << tmpset1.size() << " " << all.size() << " " << group_points_.size() << std::endl;
+  
+  vector<RevEngPoint*> remain1;
+  bool found1 = integratePlanarPoints(dir, group1, adj_elem, tol, angtol,
+				      remain1);
+  std::set<RevEngPoint*> tmpset2(remain1.begin(), remain1.end());
+  if (tmpset2.size() != remain1.size())
+    std::cout << "Point number mismatch,  remain1. " << tmpset2.size() << " " << remain1.size() << std::endl;
+  
+#ifdef DEBUG_SEGMENT
+  std::ofstream ofr1("remain1.g2");
+  ofr1 << "400 1 0 4 50 155 50 255" << std::endl;
+  ofr1 << remain1.size() << std::endl;
+  for (size_t ki=0; ki<remain1.size(); ++ki)
+    ofr1 << remain1[ki]->getPoint() << std::endl;
+#endif
+
+  if (remain1.size() > 0)
+    remaining.insert(remaining.end(), remain1.begin(), remain1.end());
+  
+  std::set<RevEngPoint*> tmpset3(remaining.begin(), remaining.end());
+  if (tmpset3.size() != remaining.size())
+    std::cout << "Point number mismatch,  remaining2. " << tmpset3.size() << " " << remaining.size() << std::endl;
+  
+#ifdef DEBUG_SEGMENT
+  std::ofstream of2("adj_planar2.g2");
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    adj_planar[ki]->writeRegionPoints(of2);
+#endif
+
+  vector<RevEngPoint*>  remain2;
+  bool found2 = defineCylindricalRegs(mainaxis, group2, min_point_in, 
+				      min_pt_reg, tol, angtol, 
+				      added_reg, hedgesfs, remain2);
+  std::set<RevEngPoint*> tmpset4(remain2.begin(), remain2.end());
+  if (tmpset4.size() != remain2.size())
+    std::cout << "Point number mismatch,  remain2. " << tmpset4.size() << " " << remain2.size() << std::endl;
+  
+#ifdef DEBUG_SEGMENT
+  std::ofstream ofr2("remain2.g2");
+  ofr2 << "400 1 0 4 50 50 155 255" << std::endl;
+  ofr2 << remain2.size() << std::endl;
+  for (size_t ki=0; ki<remain2.size(); ++ki)
+    ofr2 << remain2[ki]->getPoint() << std::endl;
+#endif
+  if (remain2.size() > 0)
+    remaining.insert(remaining.end(), remain2.begin(), remain2.end());
+  
+  std::set<RevEngPoint*> tmpset5(remaining.begin(), remaining.end());
+  if (tmpset5.size() != remaining.size())
+    std::cout << "Point number mismatch,  remaining3. " << tmpset5.size() << " " << remaining.size() << std::endl;
+
+  std::vector<RevEngPoint*> dummy;
+  connectedGroups(remaining, added_groups, false, dummy);
+  int max_num = 0;
+  int max_ix = -1;
+  for (size_t ki=0; ki<added_groups.size(); ++ki)
+    if ((int)added_groups[ki].size() > max_num)
+      {
+	max_num = (int)added_groups[ki].size();
+	max_ix = (int)ki;
+      }
+
+  if (max_ix >= 0)
+    {
+      std::swap(group_points_, added_groups[max_ix]);
+      std::swap(added_groups[max_ix], added_groups[added_groups.size()-1]);
+      added_groups.pop_back();
+      updateInfo(tol, angtol);
+    }
+  
+#ifdef DEBUG_SEGMENT
+  std::ofstream of3("added_reg_surf.g2");
+  for (size_t ki=0; ki<added_reg.size(); ++ki)
+    {
+      added_reg[ki]->writeRegionPoints(of3);
+      if (added_reg[ki]->hasSurface())
+	added_reg[ki]->writeSurface(of3);
+    }
+#endif
+  return (added_reg.size() > 0);//(found1 || found2);
+}
+
+//===========================================================================
+bool RevEngRegion::defineCylindricalRegs(Point mainaxis[3],
+					 vector<vector<RevEngPoint*> >& groups,
+					 int min_point, int min_pt_reg,
+					 double tol, double angtol,
+					 vector<shared_ptr<RevEngRegion> >& added_reg,
+					 vector<shared_ptr<HedgeSurface> >& hedgesfs,
+					 vector<RevEngPoint*>& remaining)
+//===========================================================================
+{
+  bool found = false;
+  vector<HedgeSurface*> prevsfs;   // Not feasible
+  for (size_t ki=0; ki<groups.size(); ++ki)
+    {
+      if (groups[ki].size() == 0)
+	continue;
+      
+      std::set<RevEngPoint*> tmpset4(groups[ki].begin(), groups[ki].end());
+      if (tmpset4.size() != groups[ki].size())
+	std::cout << "Point number mismatch,  axis group  " << ki << " " << tmpset4.size() << " " << groups[ki].size() << std::endl;
+      // Split into connected groups
+      vector<vector<RevEngPoint*> > connected;
+      std::vector<RevEngPoint*> dummy;
+      connectedGroups(groups[ki], connected, false, dummy);
+      for (size_t kj=0; kj<connected.size(); ++kj)
+	{
+	  // Identify adjacent regions
+	  std::set<RevEngRegion*> adj_reg;
+	  for (size_t kr=0; kr<connected[kj].size(); ++kr)
+	    {
+	      vector<ftSamplePoint*> next = connected[kj][kr]->getNeighbours();
+	      for (size_t kh=0; kh<next.size(); ++kh)
+		{
+		  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kh]);
+		  RevEngRegion *adj = curr->region();
+		  if (adj && adj != this)
+		    adj_reg.insert(adj);
+		}
+	    }
+
+	  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem;
+	  for (auto it=adj_reg.begin(); it!=adj_reg.end(); ++it)
+	    {
+	      if ((*it)->hasSurface())
+		{
+		  shared_ptr<ParamSurface> surf = (*it)->getSurface(0)->surface();
+		  shared_ptr<ElementarySurface> elemsf =
+		    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+		  if (elemsf.get())
+		    adj_elem.push_back(std::make_pair(elemsf, (*it)));
+		}
+	    }
+
+	  if (adj_elem.size() > 1)
+	    {
+	      // Try to fit a cylinder from context
+	      shared_ptr<RevEngRegion> tmp_reg(new RevEngRegion(classification_type_,
+								edge_class_type_,
+								connected[kj]));
+#ifdef DEBUG_SEGMENT
+	      std::ofstream of("cylinder_cand.g2");
+	      tmp_reg->writeRegionPoints(of);
+#endif
+	      vector<vector<RevEngPoint*> > added_groups;
+	      bool found1 = tmp_reg->contextCylinder(mainaxis, tol, min_point,
+						     min_pt_reg, angtol, 0,
+						     adj_elem, hedgesfs, prevsfs,
+						     added_groups);
+
+	      for (size_t kr=0; kr<added_groups.size(); ++kr)
+		{
+		  for (size_t kh=0; kh<added_groups[kr].size(); ++kh)
+		    added_groups[kr][kh]->setRegion(this);
+		  remaining.insert(remaining.end(), added_groups[kr].begin(),
+				   added_groups[kr].end());
+		}
+	      
+	      if (found1)
+		{
+		  found = true;
+		  tmp_reg->setRegionAdjacency();
+		  added_reg.push_back(tmp_reg);
+		}
+	      else
+		{
+		  vector<RevEngPoint*> curr_points(tmp_reg->pointsBegin(),
+						   tmp_reg->pointsEnd());
+		  for (size_t kh=0; kh<curr_points.size(); ++kh)
+		    curr_points[kh]->setRegion(this);
+		  remaining.insert(remaining.end(), curr_points.begin(),
+		  		   curr_points.end());
+		}
+	    }
+	  else
+	    remaining.insert(remaining.end(), connected[kj].begin(),
+	    		     connected[kj].end());
+	}
+    }
+  
+  return found;
+}
+
+//===========================================================================
+bool RevEngRegion::integratePlanarPoints(vector<Point>& dir,
+					 vector<vector<RevEngPoint*> >& groups,
+					 vector<pair<shared_ptr<ElementarySurface>,RevEngRegion*> >& adj_elem,
+					 double tol, double angtol,
+					 vector<RevEngPoint*>& remaining)
+//===========================================================================
+{
+  bool found = false;
+  for (size_t ki=0; ki<groups.size(); ++ki)
+    {
+      if (groups[ki].size() == 0)
+	continue;
+
+      // Split into connected groups
+      vector<vector<RevEngPoint*> > connected;
+      std::vector<RevEngPoint*> dummy;
+      connectedGroups(groups[ki], connected, false, dummy);
+      for (size_t kj=0; kj<connected.size(); ++kj)
+	{
+	  // Identify compatible adjacent plane
+	  std::set<RevEngRegion*> adj_reg;
+	  for (size_t kr=0; kr<connected[kj].size(); ++kr)
+	    {
+	      vector<ftSamplePoint*> next = connected[kj][kr]->getNeighbours();
+	      for (size_t kh=0; kh<next.size(); ++kh)
+		{
+		  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kh]);
+		  RevEngRegion *adj = curr->region();
+		  if (adj != this)
+		    adj_reg.insert(adj);
+		}
+	    }
+
+	  bool integrated = false;
+	  for (auto it=adj_reg.begin(); it!=adj_reg.end(); ++it)
+	    {
+	      for (size_t kr=0; kr<adj_elem.size(); ++kr)
+		{
+		  if (adj_elem[kr].second == (*it))
+		    {
+		      Point curr_dir = dir[ki/2];
+		      if (ki%2 == 0)
+			curr_dir *= -1;
+		      Point norm = adj_elem[kr].first->direction();
+		      Point vec = adj_elem[kr].second->getMeanNormal();
+		      if (norm*vec < 0.0)
+			norm *= -1;
+		      double ang = curr_dir.angle(norm);
+		      if (ang < angtol)
+			{
+			  // Check distance
+			  Point loc = adj_elem[kr].first->location();
+			  //double avdist = 0.0;
+			  shared_ptr<ParamSurface> surf = adj_elem[kr].first;
+			  double maxd, avd;
+			  int nmb_in, nmb2_in;
+			  vector<RevEngPoint*> in, out;
+			  vector<pair<double,double> > dist_ang;
+			  vector<double> parvals;
+			  RevEngUtils::distToSurf(connected[kj].begin(), connected[kj].end(),
+						  surf, tol, maxd, avd, nmb_in, nmb2_in,
+						  in, out, parvals, dist_ang, angtol);
+			  if (avd <= tol)
+			    {
+			      // Integrate group in adjacent plane
+			      for (size_t kh=0; kh<connected[kj].size(); ++kh)
+				{
+				  connected[kj][kh]->setPar(Vector2D(parvals[2*kh],
+								     parvals[2*kh+1]));
+				  connected[kj][kh]->setSurfaceDist(dist_ang[kh].first,
+								    dist_ang[kh].second);
+				  adj_elem[kr].second->addPoint(connected[kj][kh]);
+				}
+			      integrated = true;
+			      break;
+			    }
+			}
+		    }
+		  if (integrated)
+		    break;
+		}
+	      if (integrated)
+		break;
+	    }
+
+	  if (integrated)
+	    found = true;
+	  else
+	    remaining.insert(remaining.end(), connected[kj].begin(), connected[kj].end());
+	}
+    }
+  return found;
+}
+
+//===========================================================================
+void RevEngRegion::growPlaneOrCyl(Point mainaxis[3], int min_pt_reg,
+				  double tol, double angtol,
+				  vector<RevEngRegion*>& grown_regions,
+				  vector<HedgeSurface*>& adj_surfs,
+				  vector<vector<RevEngPoint*> >& added_groups)
+//===========================================================================
+{
+  if (associated_sf_.size() == 0)
+    return;  // No surface from which to grow
+  
+  int sfcode;
+  ClassType classtype = associated_sf_[0]->instanceType(sfcode);
+  if (classtype != Class_Plane && classtype != Class_Cylinder &&
+      classtype != Class_Cone)
+    return;
+
+  if (surfflag_ != ACCURACY_OK)
+    return;  // Grow only surfaces with good accuracy
+
+  vector<RevEngRegion*> adj_reg;
+  adj_reg.insert(adj_reg.end(), adjacent_regions_.begin(), adjacent_regions_.end());
+  int num_points = (int)group_points_.size();
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    {
+      // Get seed points
+      vector<RevEngPoint*> adj_pts =
+	adj_reg[ki]->extractNextToAdjacent(this);
+
+      // Grow
+      growFromNeighbour(mainaxis, min_pt_reg, adj_pts, tol,
+			angtol, adj_reg[ki], false);
+      if (adj_reg[ki]->numPoints() == 0)
+	{
+	  for (auto it=adj_reg[ki]->adjacent_regions_.begin();
+	       it != adj_reg[ki]->adjacent_regions_.end(); ++it)
+	    (*it)->removeAdjacentRegion(adj_reg[ki]);
+	  if (adj_reg[ki]->hasSurface())
+	    {
+	      int num_sf = adj_reg[ki]->numSurface();
+	      for (int kb=0; kb<num_sf; ++kb)
+		adj_surfs.push_back(adj_reg[ki]->getSurface(kb));
+	    }
+	  removeAdjacentRegion(adj_reg[ki]);
+	  grown_regions.push_back(adj_reg[ki]);
+	}
+      else
+	{
+	  // Make sure that the adjacent region is connected
+	  vector<vector<RevEngPoint*> > separate_groups;
+	  adj_reg[ki]->splitRegion(separate_groups);
+	  if (separate_groups.size() > 0)
+	    {
+	      added_groups.insert(added_groups.end(), separate_groups.begin(),
+				  separate_groups.end());
+	      if (adj_reg[ki]->hasSurface())
+		adj_reg[ki]->checkReplaceSurf(mainaxis, min_pt_reg, tol,
+					       angtol);
+	    }
+	}
+    }
+  if ((int)group_points_.size() > num_points)
+    checkReplaceSurf(mainaxis, min_pt_reg, tol, angtol);
+      
+#ifdef DEBUG_GROWNEIGHBOUR
+  std::ofstream of("grown_group.g2");
+  writeRegionPoints(of);
+#endif
+  if (false) //(int)group_points_.size() > num_points)
+    {
+      // Compute distance
+      shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+      double maxdist, avdist;
+      int num_in, num2_in;
+      vector<pair<double, double> > dist_ang;
+      vector<double> parvals;
+      vector<RevEngPoint*> inpt, outpt; 
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  surf, tol, maxdist, avdist, num_in, num2_in,
+			  inpt, outpt, parvals, dist_ang, angtol);
+
+      int sf_flag = defineSfFlag(0, tol, num_in, num2_in,
+				 avdist, (classtype == Class_Cylinder));
+      for (size_t kh=0; kh<group_points_.size(); ++kh)
+	{
+	  group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	  group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	}
+      setAccuracy(maxdist, avdist, num_in, num2_in);
+      setSurfaceFlag(sf_flag);
+      checkReplaceSurf(mainaxis, min_pt_reg, tol, angtol);
+    }
+}
+
+//===========================================================================
+bool RevEngRegion::segmentByAdjSfContext(Point mainaxis[3], int min_point_in, 
+					 int min_pt_reg, double tol, double angtol,
+					 vector<RevEngRegion*>& adj_planar,
+					 vector<vector<RevEngPoint*> >& added_groups)
+//===========================================================================
+{
+#ifdef DEBUG_SEGMENT
+  std::ofstream of("adj_planar.g2");
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    adj_planar[ki]->writeRegionPoints(of);
+#endif
+  //double lim = 0.1;
+  int num_pt = numPoints();
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    {
+      if ((double)adj_planar[ki]->numPoints() > min_point_in/*lim*num_pt*/)
+	{
+	  // Get seed points
+	  vector<RevEngPoint*> adj_pts = extractNextToAdjacent(adj_planar[ki]);
+	  
+	  // Grow adjacent
+	  adj_planar[ki]->growFromNeighbour(mainaxis, min_pt_reg,
+					    adj_pts, tol, angtol, this);
+	}
+#ifdef DEBUG_SEGMENT
+      std::ofstream of2("updated_planar.g2");
+      writeRegionInfo(of2);
+      adj_planar[ki]->writeRegionPoints(of2);
+#endif
+    }
+  if (numPoints() > 0 && numPoints() != num_pt)
+    {
+      splitRegion(added_groups);
+      updateInfo(tol, angtol);
+      updateRegionAdjacency();
+// #ifdef DEBUG_SEGMENT
+//       if (!isConnected())
+// 	std::cout << "Disconnect, segmentByAdjSfContext" << std::endl;
+// #endif
+      return (numPoints() > num_pt/10) ? true : false;
+      //return true;
+    }
+  else if (numPoints() == 0)
+    {
+      removeFromAdjacent();
+      clearRegionAdjacency();
+    }
+
+  return false;
+}
+
+//===========================================================================
+bool RevEngRegion::segmentByDirectionContext(int min_point_in, double tol,
+					     const Point& dir, double angtol, 
+					     vector<vector<RevEngPoint*> >& added_groups)
+//===========================================================================
+{
+  double pihalf = 0.5*M_PI;
+  vector<vector<RevEngPoint*> > pnt_groups(3);
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point norm = group_points_[ki]->getLocFuncNormal();
+      double ang = dir.angle(norm);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < angtol)
+	pnt_groups[0].push_back(group_points_[ki]);
+      else if (fabs(pihalf-ang) < angtol)
+	pnt_groups[1].push_back(group_points_[ki]);
+      else
+	pnt_groups[2].push_back(group_points_[ki]);
+    }
+
+  int num_pnt_groups = (pnt_groups[0].size() > 0) + (pnt_groups[1].size() > 0) +
+    (pnt_groups[2].size() > 0);
+  if (num_pnt_groups <= 1)
+    return false;
+#ifdef DEBUG_SEGMENT
+  std::ofstream of("point_groups.g2");
+  for (int ka=0; ka<3; ++ka)
+    {
+      if (pnt_groups[ka].size() > 0)
+	{
+	  of << "400 1 0 0" << std::endl;
+	  of << pnt_groups[ka].size() << std::endl;
+	  for (size_t ki=0; ki<pnt_groups[ka].size(); ++ki)
+	    of << pnt_groups[ka][ki]->getPoint() << std::endl;
+	}
+    }
+#endif
+  vector<vector<RevEngPoint*> > sep_groups;
+  size_t g_ix = 0;
+  for (int ka=0; ka<3; ++ka)
+    {
+      if (pnt_groups[ka].size() > 1)
+	{
+	  shared_ptr<RevEngRegion> reg(new RevEngRegion(classification_type_,
+							edge_class_type_,
+							pnt_groups[ka]));
+	  vector<vector<RevEngPoint*> > curr_sep_groups;
+	  reg->splitRegion(curr_sep_groups);
+	  sep_groups.push_back(reg->getPoints());
+	  if (curr_sep_groups.size() > 0)
+	    sep_groups.insert(sep_groups.end(), curr_sep_groups.begin(),
+			      curr_sep_groups.end());
+	  for (; g_ix<sep_groups.size(); ++g_ix)
+	    for (size_t kj=0; kj<sep_groups[g_ix].size(); ++kj)
+	      sep_groups[g_ix][kj]->unsetRegion();
+	}
+      else if (pnt_groups[ka].size() == 1)
+	pnt_groups[ka][0]->unsetRegion();
+    }
+
+#ifdef DEBUG_SEGMENT
+  std::ofstream of2("sep_groups.g2");
+  for (size_t kj=0; kj<sep_groups.size(); ++kj)
+    {
+      of2 << "400 1 0 0" << std::endl;
+      of2 << sep_groups[kj].size() << std::endl;
+      for (size_t ki=0; ki<sep_groups[kj].size(); ++ki)
+	of2 << sep_groups[kj][ki]->getPoint() << std::endl;
+    }
+#endif
+
+  int max_ix = -1;
+  int num_pnts = 0;
+  for (size_t ki=0; ki<sep_groups.size(); ++ki)
+    {
+      if ((int)sep_groups[ki].size() > num_pnts)
+	{
+	  num_pnts = (int)sep_groups[ki].size();
+	  max_ix = (int)ki;
+	}
+    }
+
+  for (size_t ki=0; ki<sep_groups.size(); ++ki)
+    {
+      if ((int)ki == max_ix)
+	{
+	  group_points_.clear();
+	  group_points_ = sep_groups[max_ix];
+	  for (size_t kj=0; kj<group_points_.size(); ++kj)
+	    group_points_[kj]->setRegion(this);
+	  updateInfo(tol, angtol);
+	}
+      else
+	{
+	  for (size_t kj=0; kj<sep_groups[ki].size(); ++kj)
+	    sep_groups[ki][kj]->unsetRegion();
+	  added_groups.push_back(sep_groups[ki]);
+	}
+    }
+
+  return (added_groups.size() > 0);
+}
+
+//===========================================================================
+void RevEngRegion::getPCA(double lambda[3], Point& eigen1, Point& eigen2,
+			  Point& eigen3)
+//===========================================================================
+{
+  return getPCA(group_points_, lambda, eigen1, eigen2, eigen3);
+}
+
+//===========================================================================
+void RevEngRegion::getPCA(vector<RevEngPoint*>& points, double lambda[3], 
+			  Point& eigen1, Point& eigen2, Point& eigen3)
+//===========================================================================
+{
+  // PCA analysis of group points
+  Vector3D xyz = points[0]->getPoint();
+  Point pos0(xyz[0], xyz[1], xyz[2]);
+  vector<Point> pnts;
+  pnts.reserve(points.size());
+  //double wgt = 1.0/(double)points.size();
+  for (size_t ki=1; ki<points.size(); ++ki)
+    {
+      xyz = points[ki]->getPoint();
+      Point pos(xyz[0], xyz[1], xyz[2]);
+      pnts.push_back(pos);
+    }
+
+  double eigenvec[3][3];
+  RevEngUtils::principalAnalysis(pos0, pnts, lambda, eigenvec);
+  eigen1 = Point(eigenvec[0][0], eigenvec[0][1], eigenvec[0][2]);
+  eigen2 = Point(eigenvec[1][0], eigenvec[1][1], eigenvec[1][2]);
+  eigen3 = Point(eigenvec[2][0], eigenvec[2][1], eigenvec[2][2]);
+}
+
+
+//===========================================================================
+shared_ptr<SplineSurface> RevEngRegion::surfApprox(vector<RevEngPoint*>& points,
+						   const BoundingBox& bbox)
+//===========================================================================
+{
+  // PCA analysis of given points to orient plane for parametrerization
+  Vector3D xyz = points[0]->getPoint();
+  Point pos0(xyz[0], xyz[1], xyz[2]);
+  vector<Point> pnts;
+  pnts.reserve(points.size());
+  Point vec(0.0, 0.0, 0.0);
+  double wgt = 1.0/(double)(group_points_.size());
+  for (size_t ki=1; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pos(xyz[0], xyz[1], xyz[2]);
+      pnts.push_back(pos);
+      Point vec0 = points[ki]->minCurvatureVec();
+      if (vec0*vec < 0.0)
+	vec0 *= -1;
+      vec += wgt*vec0;
+    }
+  double lambda[3];
+  double eigenvec[3][3];
+  RevEngUtils::principalAnalysis(pos0, pnts, lambda, eigenvec);
+  Point eigen3(eigenvec[2][0], eigenvec[2][1], eigenvec[2][2]);
+  Point ydir = eigen3.cross(vec);
+  ydir.normalize_checked();
+  Point xdir = ydir.cross(eigen3);
+  xdir.normalize_checked();
+
+  // Parameterize points based on planar surface
+  pnts.push_back(pos0);
+  vector<double> data;
+  vector<double> param;
+  RevEngUtils::parameterizeWithPlane(pnts, bbox, xdir, ydir,
+				     data, param);
+
+  // Surface approximation
+  int order = 3;
+  double belt = 0.1*bbox.low().dist(bbox.high());
+  shared_ptr<SplineSurface> surf = RevEngUtils::surfApprox(data, 3, param, order,
+							   order, order, order, belt);
+return surf;
+}
+
+//===========================================================================
+void RevEngRegion::collect(RevEngPoint *pt, RevEngRegion *prev)
+//===========================================================================
+{
+  if (pt->hasRegion() && pt->region() != this)
+    return;  // Cannot grow
+  group_points_.push_back(pt);
+  if (classification_type_ == CLASSIFICATION_UNDEF)
+    return; // Cannot grow
+  int type = pt->surfaceClassification(classification_type_);
+  double meancurv = pt->meanCurvature();
+  double gausscurv = pt->GaussCurvature();
+  if (type == C1_UNDEF)  // SI_UNDEF == C1_UNDEF
+    return; // Cannot grow
+
+  vector<RevEngPoint*> grouped;
+  grouped.push_back(pt);
+  Vector3D xyz = pt->getPoint();
+  Point xyz2(xyz[0], xyz[1], xyz[2]);
+  bbox_.addUnionWith(xyz2);
+  if (normalcone_.dimension() == 0)
+    normalcone_ = DirectionCone(pt->getLocFuncNormal());
+  else
+    normalcone_.addUnionWith(pt->getLocFuncNormal());
+  if (normalcone2_.dimension() == 0)
+    normalcone2_ = DirectionCone(pt->getTriangNormal());
+  else
+    normalcone2_.addUnionWith(pt->getTriangNormal());
+  bool planar = planartype();
+  for (size_t kj=0; kj<grouped.size(); ++kj)
+    {
+      vector<ftSamplePoint*> next = grouped[kj]->getNeighbours();
+      for (size_t ki=0; ki<next.size(); ++ki)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[ki]);
+	  if (!curr)
+	    continue;  // Should not happen
+	  if (curr->isOutlier())
+	    continue;
+	  if (curr->hasRegion() && curr->region() != prev)
+	    continue;  // Already belonging to a segment
+	  if (curr->isEdge(edge_class_type_))
+	    continue;  // An edge point
+	  int type2 = curr->surfaceClassification(classification_type_);
+	  if (type2 != type)
+	    continue;   // Different classification
+	  if (classification_type_ == CLASSIFICATION_SHAPEINDEX &&
+	      (!planar) && meancurv*curr->meanCurvature() < 0.0)
+	    continue;  // Not compatible with one primary surface
+	  double curv1 = meancurv*gausscurv;
+	  double curv2 = curr->meanCurvature()*curr->GaussCurvature();
+	  if (classification_type_ == CLASSIFICATION_SHAPEINDEX &&
+	      (!planar) && curv1*curv2 < 0.0)
+	    continue;  // Not compatible with one primary surface
+
+	  // Add to region
+	  curr->setRegion(this);
+	  group_points_.push_back(curr);
+	  xyz = curr->getPoint();
+	  xyz2 = Point(xyz[0], xyz[1], xyz[2]);
+	  bbox_.addUnionWith(xyz2);
+	  normalcone_.addUnionWith(curr->getLocFuncNormal());
+	  normalcone2_.addUnionWith(curr->getTriangNormal());
+
+	  // Continue growing from this point
+	  grouped.push_back(curr);
+	}
+    }
+#ifdef DEBUG_COLLECT
+  std::ofstream of("collected_group.g2");
+  writeRegionPoints(of);
+  vector<RevEngPoint*> bd_pts = extractBdPoints();
+  of << "400 1 0 4 0 0 0 255" << std::endl;
+  of << bd_pts.size() << std::endl;
+  for (size_t kj=0; kj<bd_pts.size(); ++kj)
+    of << bd_pts[kj]->getPoint() << std::endl;
+#endif
+  // Principal curvature summary
+  updateInfo();
+}
+
+//===========================================================================
+BoundingBox RevEngRegion::getParameterBox()
+//===========================================================================
+{
+  BoundingBox parbox(2);
+  if (associated_sf_.size() == 0)
+    return parbox;   // No surface means that the points are not parameterized
+
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector2D par = group_points_[ki]->getPar();
+      Point par2(par[0], par[1]);
+      parbox.addUnionWith(par2);
+    }
+  return parbox;
+}
+
+
+//===========================================================================
+RevEngPoint* RevEngRegion::seedPointPlane(int min_next, double rfac, double angtol)
+//===========================================================================
+{
+  double min_in = 0;
+  int min_ix = -1;
+  int multfac = 10;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      double local_len = group_points_[ki]->getMeanEdgLen();
+      Point normal = group_points_[ki]->getLocFuncNormal();
+      double radius = 2.0*rfac*local_len;
+      vector<RevEngPoint*> nearpts;
+      group_points_[ki]->fetchClosePoints2(radius, min_next, 5*min_next,
+					   nearpts, this);
+
+      // Count deviant points
+      int deviant = 0;
+      for (size_t kj=0; kj<nearpts.size(); ++kj)
+      {
+	Point curr_norm = nearpts[kj]->getLocFuncNormal();
+	double ang = curr_norm.angle(normal);
+	if (nearpts[kj]->region() != this || ang > angtol)
+	  ++deviant;
+      }
+
+      if (deviant == 0)
+	return group_points_[ki];   // Seed point found
+
+      if ((int)nearpts.size() - deviant > min_in)
+	{
+	  min_in = (int)nearpts.size() - deviant;
+	  min_ix = (int)ki;
+	  if (min_in < multfac*min_next)
+	    break;
+	}
+    }
+  return (min_ix >= 0) ? group_points_[min_ix] : 0;
+}
+
+//===========================================================================
+void RevEngRegion::growLocalPlane(Point mainaxis[3],
+				  double tol, vector<RevEngPoint*>& plane_pts,
+				  shared_ptr<Plane>& plane_out)
+//===========================================================================
+{
+  // Get seed point for local grow
+  int min_next = std::max(10, (int)group_points_.size()/100);
+  double rfac = 3;
+  double angtol = 0.1;
+  RevEngPoint* seed = seedPointPlane(min_next, rfac, angtol);
+  if (!seed)
+    return;
+ 
+#ifdef DEBUG_SEGMENT
+  std::ofstream of("seed.g2");
+  of << "400 1 0 4 200 0 55 255" << std::endl;
+  of << "1" << std::endl;
+  of << seed->getPoint() << std::endl;
+#endif
+  // Fetch nearby points belonging to the same region
+  double local_len = seed->getMeanEdgLen();
+  double radius = 2.0*rfac*local_len;
+  vector<RevEngPoint*> nearpts;
+  seed->fetchClosePoints2(radius, min_next, 5*min_next, nearpts, this);
+  nearpts.insert(nearpts.begin(), seed);
+  if ((int)nearpts.size() < min_next/2)
+    return;
+
+  // Approximate with plane
+  shared_ptr<Plane> plane = computePlane(nearpts, avnorm_, mainaxis);
+
+  double eps = 1.0e-6;
+  double maxdist, avdist;
+  int num_inside, num2_inside;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  vector<RevEngPoint*> inpt, outpt;
+  RevEngUtils::distToSurf(nearpts.begin(), nearpts.end(),
+			  plane, tol, maxdist, avdist, num_inside, num2_inside,
+			  inpt, outpt, parvals, dist_ang, angtol);
+
+  if (maxdist > tol)
+    return;   // For the time being
+
+  
+  for (size_t ki=0; ki<nearpts.size(); ++ki)
+    nearpts[ki]->setVisited();
+  size_t prev_size = nearpts.size();
+  while (true)
+    {
+      for (size_t ki=0; ki<nearpts.size(); ++ki)
+	{
+	  vector<ftSamplePoint*> next = nearpts[ki]->getNeighbours();
+	  for (size_t kj=0; kj<next.size(); ++kj)
+	    {
+	      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next[kj]);
+	      if ((!pt->hasRegion()) || pt->region() != this)
+		continue;
+	      if (pt->visited())
+		continue;
+	      Vector3D xyz = pt->getPoint();
+
+	      double upar, vpar, dist;
+	      Point close;
+	      plane->closestPoint(Point(xyz[0],xyz[1],xyz[2]), upar, vpar, close, dist, eps);
+	      pt->setVisited();
+	      if (dist < tol)
+		nearpts.push_back(pt);
+	    }
+	}
+
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  if (std::find(nearpts.begin(), nearpts.end(), group_points_[ki]) == nearpts.end())
+	    group_points_[ki]->unsetVisited();
+	}
+
+      if (nearpts.size() == prev_size)
+	break;
+      
+      shared_ptr<Plane> plane2 = computePlane(nearpts, avnorm_, mainaxis);
+
+      double maxdist2, avdist2;
+      int num_inside2, num2_inside2;
+      vector<pair<double, double> > dist_ang2;
+      vector<double> parvals2;
+     RevEngUtils::distToSurf(nearpts.begin(), nearpts.end(),
+			     plane2, tol, maxdist2, avdist2, num_inside2,
+			     num2_inside2, inpt,
+			     outpt, parvals2, dist_ang2, angtol);
+      if (maxdist2 > tol)
+	break;
+      
+      prev_size = nearpts.size();
+      plane = plane2;
+      maxdist = maxdist2;
+      avdist = avdist2;
+      num_inside = num_inside2;
+      parvals = parvals2;
+      dist_ang = dist_ang2;
+    }
+
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    group_points_[ki]->unsetVisited();
+#ifdef DEBUG_EXTRACT  
+  std::ofstream of2("plane_pts.g2");
+  of2 << "400 1 0 4 100 155 0 255" << std::endl;
+  of2 << nearpts.size() << std::endl;
+  for (size_t ki=0; ki<nearpts.size(); ++ki)
+    of2 << nearpts[ki]->getPoint() << std::endl;
+
+  plane->writeStandardHeader(of2);
+  plane->write(of2);
+#endif
+  plane_pts = nearpts;
+  plane_out = plane;
+
+  int stop_break = 1;
+
+}
+
+//===========================================================================
+void RevEngRegion::segmentByPlaneGrow(Point mainaxis[3], double tol,
+				      double angtol, int min_pt, 
+				      vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				      vector<HedgeSurface*>& prevsfs,
+				      vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  // Extract group of planar points and associated plane
+  int min_nmb_pts = std::max((int)group_points_.size()/100, 10);
+  vector<RevEngPoint*> plane_pts;
+  shared_ptr<Plane> plane;
+  growLocalPlane(mainaxis, tol, plane_pts, plane);
+  if ((int)plane_pts.size() < min_nmb_pts || plane_pts.size() == group_points_.size())
+    return;
+
+  // Fetch accuracy information
+   double maxd, avd;
+   int num_in, num2_in;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  vector<RevEngPoint*> inpt, outpt;
+  RevEngUtils::distToSurf(plane_pts.begin(), plane_pts.end(),
+			  plane, tol, maxd, avd, num_in, num2_in, inpt,
+			  outpt, parvals, dist_ang, angtol);
+  
+  // Extract remaining points
+  for (size_t ki=0; ki<plane_pts.size(); ++ki)
+    plane_pts[ki]->setVisited();
+
+  vector<RevEngPoint*> remaining_pts;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      if (!group_points_[ki]->visited())
+	{
+	  remaining_pts.push_back(group_points_[ki]);
+	  removePoint(group_points_[ki]);
+	}
+    }
+
+  for (size_t ki=0; ki<plane_pts.size(); ++ki)
+    plane_pts[ki]->unsetVisited();
+
+  if (hasSurface())
+    {
+      // No longer valid
+      prevsfs.insert(prevsfs.end(), associated_sf_.begin(), associated_sf_.end());
+      clearSurface();
+    }
+  
+  if ((int)plane_pts.size() >= min_pt)
+    {
+      // Register current plane
+      int sf_flag = defineSfFlag(0, tol, num_in, num2_in,
+				 avd, false);
+      for (size_t ki=0; ki<plane_pts.size(); ++ki)
+	{
+	  plane_pts[ki]->setPar(Vector2D(parvals[2*ki],parvals[2*ki+1]));
+	  plane_pts[ki]->setSurfaceDist(dist_ang[ki].first, dist_ang[ki].second);
+	}
+      setAccuracy(maxd, avd, num_in, num2_in);
+      shared_ptr<HedgeSurface> hedge(new HedgeSurface(plane, this));
+      setHedge(hedge.get());
+      hedgesfs.push_back(hedge);
+      setSurfaceFlag(sf_flag);
+    }
+
+  // Store as base surface and primary surface
+  setBaseSf(plane, maxd, avd, num_in, num2_in);
+
+  updateInfo(tol, angtol);
+
+  // Distribute remaining points into groups and create regions
+  shared_ptr<RevEngRegion> reg(new RevEngRegion(classification_type_,
+						 edge_class_type_,
+						 remaining_pts));
+  vector<vector<RevEngPoint*> > connected;
+  reg->splitRegion(connected);
+  out_groups.push_back(reg->getPoints());
+  if (connected.size() > 0)
+    out_groups.insert(out_groups.end(), connected.begin(), connected.end());
+}
+
+
+
+//===========================================================================
+bool RevEngRegion::extractPlane(Point mainaxis[3],
+				double tol, int min_pt, int min_pt_reg,
+				double angtol, int prefer_elementary,
+				vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				vector<HedgeSurface*>& prevsfs,
+				vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  // std::ofstream of("curr_region.g2");
+  // writeRegionInfo(of);
+  
+  // std::ofstream of2("curr_normals.g2");
+  // writeUnitSphereInfo(of2);
+
+#ifdef DEBUG
+  if (normalcone_.greaterThanPi())
+    {
+      std::cout << "Greater than pi" << std::endl;
+      std::ofstream ofpi("pi_region.g2");
+      writeRegionInfo(ofpi);
+    }
+#endif
+  
+  bool found = false;
+  int min_nmb = 20;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+
+  // vector<RevEngPoint*> in, out;
+  // analysePlaneProperties(normal, angtol, in, out);
+  
+  // shared_ptr<Plane> surf1(new Plane(pos, normal1));
+  // shared_ptr<Plane> surf(new Plane(pos, normal));
+
+  // Point pos2 = pos;
+  // Point normal2;
+  // vector<pair<vector<RevEngPoint*>::iterator,
+  // 	      vector<RevEngPoint*>::iterator> > group;
+  // group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  // RevEngUtils::computePlane(group, pos2, normal2);
+  // shared_ptr<Plane> surf2(new Plane(pos2, normal2));
+  
+  // vector<RevEngPoint*> in3, out3;
+  // analysePlaneProperties(normal3, angtol, in3, out3);
+
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*>  > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+  
+  shared_ptr<Plane> surf3 = computePlane(group_points_, avnorm_, mainaxis); //(new Plane(pos3, normal3));
+
+  // Check accuracy
+  // double maxdist, avdist;
+  // double maxdist1, avdist1;
+  // double maxdist2, avdist2;
+  double maxdist3, avdist3;
+  int num_inside3, num2_inside3; //, num_inside1, num_inside2, num_inside3;
+  vector<RevEngPoint*> inpt, outpt;
+  // RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+  // 			  surf, tol, maxdist, avdist, num_inside);
+  // RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+  // 			  surf1, tol, maxdist1, avdist1, num_inside1);
+  // RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+  // 			  surf2, tol, maxdist2, avdist2, num_inside2);
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  surf3, tol, maxdist3, avdist3, num_inside3, num2_inside3,
+			  inpt, outpt, parvals, dist_ang, angtol);
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofd("in_out_plane.g2");
+  ofd << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd << inpt.size() << std::endl;
+  for (size_t kr=0; kr<inpt.size(); ++kr)
+    ofd << inpt[kr]->getPoint() << std::endl;
+  ofd << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd << outpt.size() << std::endl;
+  for (size_t kr=0; kr<outpt.size(); ++kr)
+    ofd << outpt[kr]->getPoint() << std::endl;
+#endif
+  
+  if (inpt.size() > group_points_.size()/3 && (int)inpt.size() > min_nmb &&
+      (normalcone_.angle() > angtol ||
+       normalcone_.centre().angle(surf3->getNormal()) > angtol) &&
+      (!(basesf_.get() && basesf_->instanceType() == Class_Cylinder)))
+    {
+      vector<RevEngPoint*> ang_points;
+      double dtol = std::min(0.5*tol, 1.5*avdist3);
+      identifyAngPoints(dist_ang, angtol, dtol, ang_points);
+      if (ang_points.size() < group_points_.size()/2)
+	{
+	  extractSpesPoints(ang_points, out_groups, true);
+	  
+	  if (out_groups.size() > 0)
+	    {
+	      // Ensure connected region
+	      vector<vector<RevEngPoint*> > separate_groups;
+	      splitRegion(separate_groups);
+	      if (separate_groups.size() > 0)
+		{
+		  out_groups.insert(out_groups.end(), separate_groups.begin(),
+				    separate_groups.end());
+		}
+	  
+	      shared_ptr<Plane> plane_in =
+		computePlane(group_points_, avnorm_, mainaxis);
+	      vector<RevEngPoint*> inpt_in, outpt_in; //, inpt2, outpt2;
+	      vector<pair<double, double> > dist_ang_in;
+	      vector<double> parvals_in;
+	      double maxd_in, avd_in;
+	      int num2_in, num2_in2;
+	      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+				      plane_in, tol, maxd_in, avd_in, num2_in, num2_in2,
+				      inpt_in, outpt_in, parvals_in, dist_ang_in, angtol);
+#ifdef DEBUG_EXTRACT  
+  	      std::ofstream ofd2("in_out_plane2.g2");
+	      ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+	      ofd2 << inpt_in.size() << std::endl;
+	      for (size_t kr=0; kr<inpt_in.size(); ++kr)
+		ofd2 << inpt_in[kr]->getPoint() << std::endl;
+	      ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+	      ofd2 << outpt_in.size() << std::endl;
+	      for (size_t kr=0; kr<outpt_in.size(); ++kr)
+		ofd2 << outpt_in[kr]->getPoint() << std::endl;
+#endif
+	      // if (num2_in > num_inside3 || avd_in < avdist3)
+	      // 	{
+	      std::swap(surf3, plane_in);
+	      std::swap(num_inside3, num2_in);
+	      std::swap(num2_inside3, num2_in2);
+	      std::swap(avdist3, avd_in);
+	      std::swap(maxdist3, maxd_in);
+	      std::swap(parvals, parvals_in);
+	      std::swap(dist_ang, dist_ang_in);
+	      // 	  std::cout << "Plane swap" << std::endl;
+	      // 	  std::cout << group_points_.size() << " " << num2_in << " ";
+	      // std::cout<< num_inside3 << " " << avd_in << " " << avdist3;
+	      // 	  std::cout << " " << maxd_in << " " << maxdist3 << std::endl;
+	      // }
+	    }
+	}
+    }
+
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  //double len = low.dist(high);
+  //surf3->setParameterBounds(-len, -len, len, len);
+#ifdef DEBUG_EXTRACT  
+  std::ofstream plane("curr_plane.g2");
+  surf3->writeStandardHeader(plane);
+  surf3->write(plane);
+#endif
+  //int num = (int)group_points_.size();
+  int sf_flag = defineSfFlag(0, tol, num_inside3, num2_inside3,
+			     avdist3, false);
+  if (sf_flag < ACCURACY_POOR)
+    {
+      found = true;
+      for (size_t kh=0; kh<group_points_.size(); ++kh)
+	{
+	  group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	  group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	}
+      setAccuracy(maxdist3, avdist3, num_inside3, num2_inside3);
+
+#ifdef DEBUG
+      std::cout << "Plane. N1: " << num << ", N2: " << num_inside3 << ", max: " << maxdist3 << ", av: " << avdist3 << std::endl;
+#endif
+      
+      shared_ptr<HedgeSurface> hedge(new HedgeSurface(surf3, this));
+      for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+	prevsfs.push_back(associated_sf_[kh]);
+      setHedge(hedge.get());
+      hedgesfs.push_back(hedge);
+      setSurfaceFlag(sf_flag);
+    }
+  if (!basesf_.get() ||
+      (num_inside3 >= num_in_base_ && avdist3 < avdist_base_))
+    setBaseSf(surf3, maxdist3, avdist3, num_inside3, num2_inside3);
+
+  return found;
+}
+
+
+//===========================================================================
+shared_ptr<Plane> RevEngRegion::computePlane(vector<RevEngPoint*>& points,
+					     const Point& norm_dir, Point mainaxis[3])
+//===========================================================================
+{
+  Point normal1 = normalcone_.centre();
+  Point normal(0.0, 0.0, 0.0);
+  Point pos(0.0, 0.0, 0.0);
+  double wgt = 1.0/(double)(points.size());
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Point curr = points[ki]->getLocFuncNormal();
+      Vector3D xyz = points[ki]->getPoint();
+      normal += wgt*curr;
+      pos += wgt*Point(xyz[0], xyz[1], xyz[2]);
+    }
+  
+  ImplicitApprox impl;
+  impl.approx(points, 1);
+  Point pos3, normal3;
+  bool found = impl.projectPoint(pos, normal, pos3, normal3);
+  if (!found)
+    {
+      pos3 = pos;
+      normal3 = normal;
+      normal3.normalize();
+    }
+  if (normal3*norm_dir < 0.0)
+    normal3 *= -1.0;
+
+  // Define x-axis
+  int ix = -1;
+  double minang = M_PI;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = mainaxis[ka].angle(normal3);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < minang)
+	{
+	  minang = ang;
+	  ix = ka;
+	}
+    }
+
+  Point Cy = mainaxis[(ix+1)%3].cross(normal3);
+  Point Cx = normal3.cross(Cy);
+  
+  shared_ptr<Plane> surf(new Plane(pos3, normal3, Cx));
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  //double len = low.dist(high);
+  //surf->setParameterBounds(-len, -len, len, len);
+  
+  bool plane_project = false;
+  if (plane_project)
+    {
+      vector<pair<vector<RevEngPoint*>::iterator,
+		  vector<RevEngPoint*>::iterator> > group;
+      group.push_back(std::make_pair(points.begin(), points.end()));
+      Point vec1, vec2;
+      surf->getSpanningVectors(vec1, vec2);
+      vector<Point> projected1, projected2;
+      double maxdp1, avdp1, maxdp2, avdp2;
+      RevEngUtils::projectToPlane(group, vec1, pos3, projected1, maxdp1, avdp1);
+      RevEngUtils::projectToPlane(group, vec2, pos3, projected2, maxdp2, avdp2);
+
+#ifdef DEBUG_EXTRACT
+      std::ofstream ofp3("plane_project.g2");
+      ofp3 << "400 1 0 4 255 0 0 255" << std::endl;
+      ofp3 << projected1.size() << std::endl;
+      for (size_t kr=0; kr<projected1.size(); ++kr)
+	ofp3 << projected1[kr] << std::endl;
+      ofp3 << "400 1 0 4 255 0 0 255" << std::endl;
+      ofp3 << projected2.size() << std::endl;
+      for (size_t kr=0; kr<projected2.size(); ++kr)
+	ofp3 << projected2[kr] << std::endl;
+#endif
+    }
+  return surf;
+}
+
+//===========================================================================
+void RevEngRegion::getDistAndAng(vector<pair<double,double> >& distang)
+//===========================================================================
+{
+  distang.resize(group_points_.size());
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      double dist, ang;
+      group_points_[ki]->getSurfaceDist(dist, ang);
+      distang[ki] = std::make_pair(dist, ang);
+    }
+}
+
+//===========================================================================
+bool RevEngRegion::possiblePlane(double angtol, double inlim)
+//===========================================================================
+{
+  if (planartype())
+    return true;
+
+  double wgt = 1.0/(double)group_points_.size();
+  Point avnorm(0.0, 0.0, 0.0);
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point curr = group_points_[ki]->getLocFuncNormal();
+      avnorm += wgt*curr;
+    }
+
+  int nmb_in = 0;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point curr = group_points_[ki]->getLocFuncNormal();
+      double ang = curr.angle(avnorm);
+      if (ang <= angtol)
+	++nmb_in;
+    }
+
+  double frac = (double)nmb_in/(double)group_points_.size();
+  return (frac >= inlim);
+}
+
+//===========================================================================
+vector<RevEngRegion*> RevEngRegion::fetchAdjacentPlanar()
+//===========================================================================
+{
+  vector<RevEngRegion*> adjacent_planar;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      if ((*it)->hasSurface())
+	{
+	  shared_ptr<ParamSurface> sf = (*it)->getSurface(0)->surface();
+	  if (sf->instanceType() == Class_Plane)
+	    adjacent_planar.push_back(*it);
+	}
+    }
+
+  return adjacent_planar;
+}
+
+//===========================================================================
+vector<RevEngRegion*> RevEngRegion::fetchAdjacentCylindrical()
+//===========================================================================
+{
+  vector<RevEngRegion*> adjacent_cylindrical;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      if ((*it)->hasSurface())
+	{
+	  shared_ptr<ParamSurface> sf = (*it)->getSurface(0)->surface();
+	  if (sf->instanceType() == Class_Cylinder)
+	    adjacent_cylindrical.push_back(*it);
+	}
+    }
+
+  return adjacent_cylindrical;
+}
+
+//===========================================================================
+Point RevEngRegion::directionFromAdjacent(double angtol)
+//===========================================================================
+{
+  Point dir;
+  vector<std::pair<Point,int> > all_dir;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      if ((*it)->hasSurface())
+	{
+	  shared_ptr<ParamSurface> sf = (*it)->getSurface(0)->surface();
+	  if (sf->instanceType() == Class_Plane || sf->instanceType() == Class_Cylinder)
+	    {
+	      shared_ptr<ElementarySurface> elem =
+		dynamic_pointer_cast<ElementarySurface,ParamSurface>(sf);
+	      Point curr_dir = elem->direction();
+	      size_t kj;
+	      for (kj=0; kj<all_dir.size(); ++kj)
+		{
+		  double ang = curr_dir.angle(all_dir[kj].first);
+		  if (M_PI-ang < ang)
+		    {
+		      curr_dir *= -1.0;
+		      ang = M_PI - ang;
+		    }
+		  if (ang < angtol)
+		    {
+		      int num = (*it)->numPoints();
+		      double fac = 1.0/(double)(all_dir[kj].second+num);
+		      curr_dir = fac*(all_dir[kj].second*all_dir[kj].first + num*curr_dir);
+		      break;
+		    }
+		}
+	      if (kj == all_dir.size())
+		all_dir.push_back(std::make_pair(curr_dir, (*it)->numPoints()));
+	    }
+	}
+    }
+
+  for (size_t ki=0; ki<all_dir.size(); ++ki)
+    for (size_t kj=ki+1; kj<all_dir.size(); ++kj)
+      if (all_dir[kj].second > all_dir[ki].second)
+	std::swap(all_dir[ki], all_dir[kj]);
+
+  if (all_dir.size() > 0)
+    dir = all_dir[0].first;
+
+  return dir;
+}
+
+//===========================================================================
+bool RevEngRegion::potentialBlend(double angtol)
+//===========================================================================
+{
+  double pihalf = 0.5*M_PI;
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+  for (size_t ki=0; ki<adj_elem.size(); ++ki)
+    {
+      ClassType type1 = adj_elem[ki].first->instanceType();
+      Point dir1 = adj_elem[ki].first->direction();
+      if (type1 != Class_Plane && type1 != Class_Cylinder)
+	continue;
+      for (size_t kj=ki+1; kj<adj_elem.size(); ++kj)
+	{
+	  ClassType type2 = adj_elem[kj].first->instanceType();
+	  if (type2 != Class_Plane && type2 != Class_Cylinder)
+	    continue;
+	  if (type1 == Class_Cylinder && type2 == Class_Cylinder)
+	    continue;
+	  Point dir2 = adj_elem[kj].first->direction();
+	  double ang = dir1.angle(dir2);
+	  if (type1 == Class_Plane && type2 == Class_Plane)
+	    ang = std::min(ang, M_PI-ang);
+	  else
+	    ang = fabs(pihalf - ang);
+	  if (ang > angtol)
+	    return true;
+	}
+    }
+  return false;
+}
+
+//===========================================================================
+void RevEngRegion::neighbourBlends(vector<shared_ptr<CurveOnSurface> >& cvs,
+				    double width, double tol,
+				    vector<RevEngRegion*>& new_blends)
+//===========================================================================
+{
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      if ((*it)->hasAssociatedBlend())
+	continue;
+      if ((*it)->hasRevEdges())
+	continue;
+      int in_blend = 0;
+      if ((*it)->isInBlend(cvs, width, tol, in_blend))
+	new_blends.push_back(*it);
+    }
+}
+
+//===========================================================================
+bool RevEngRegion::isInBlend(vector<shared_ptr<CurveOnSurface> >& cvs,
+			     double width, double tol, int& in_blend)
+//===========================================================================
+{
+  double lim = 0.9;
+  in_blend = 0;
+  double tmin = cvs[0]->startparam();
+  double tmax = cvs[cvs.size()-1]->endparam();
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      double tpar, dist=std::numeric_limits<double>::max();;
+      Vector3D xyz = group_points_[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      Point close;
+      for (size_t kj=0; kj<cvs.size(); ++kj)
+	{
+	  cvs[kj]->closestPoint(pt, cvs[kj]->startparam(), cvs[kj]->endparam(),
+				tpar, close, dist);
+	  if (tpar > tmin && tpar < tmax && dist < width+tol)
+	    in_blend++;
+	}
+    }
+  if ((double)in_blend >= lim*(double)group_points_.size())
+    return true;
+  else
+    return false;
+}
+
+//===========================================================================
+void RevEngRegion::getAdjacentElemInfo(vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj_elem,
+				       vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj_elem_base)
+//===========================================================================
+{
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      bool found = false;
+      if ((*it)->hasSurface())
+	{
+	  shared_ptr<ParamSurface> sf = (*it)->getSurface(0)->surface();
+	  shared_ptr<ElementarySurface> elem =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(sf);
+	  if (elem.get())
+	    {
+	      adj_elem.push_back(std::make_pair(elem,*it));
+	      found = true;
+	    }
+	}
+      
+      if ((*it)->hasBaseSf() && (!found))
+	{
+	  shared_ptr<ParamSurface> sf = (*it)->getBase();
+	  shared_ptr<ElementarySurface> elem =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(sf);
+	  if (elem.get())
+	    {
+	      adj_elem_base.push_back(std::make_pair(elem, *it));
+	      found = true;
+	    }
+	}
+      
+    }
+
+#ifdef DEBUG_ADJACENT
+  std::ofstream of("adj_sf_pts.g2");
+  for (size_t ki=0; ki<adj_elem.size(); ++ki)
+    adj_elem[ki].second->writeRegionPoints(of);
+#endif
+}
+
+//===========================================================================
+bool RevEngRegion::possibleCylinder(double angtol, double inlim)
+//===========================================================================
+{
+  // if (cylindertype())
+  //   return true;
+
+  double wgt = 1.0/(double)group_points_.size();
+  Point avvec = wgt*group_points_[0]->minCurvatureVec();
+  for (size_t ki=1; ki<group_points_.size(); ++ki)
+    {
+      Point curr = group_points_[ki]->minCurvatureVec();
+      if (curr*avvec < 0.0)
+	curr *= -1;
+      avvec += wgt*curr;
+    }
+
+  int nmb_in = 0;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point curr = group_points_[ki]->minCurvatureVec();
+      double ang = curr.angle(avvec);
+      ang = std::min(ang, M_PI-ang);
+      if (ang <= angtol)
+	++nmb_in;
+    }
+
+  double frac = (double)nmb_in/(double)group_points_.size();
+  return (frac >= inlim);
+}
+
+//===========================================================================
+bool RevEngRegion::possibleCone(double tol, double inlim)
+//===========================================================================
+{
+  return true;  // For the time being
+}
+
+//===========================================================================
+bool RevEngRegion::possibleTorus(double tol, double inlim)
+//===========================================================================
+{
+  // if (planartype() || cylindertype())
+  //   return false;
+  // Compute curvature variations
+  double k1mean = 0.0, k2mean = 0.0;
+  double k1_1 = 0.0, k1_2 = 0.0, k2_1 = 0.0, k2_2 = 0.0;
+  double d1 = 0.0, d2 = 0.0;
+  double wgt = 1.0/(double)group_points_.size();
+  Point vec(0.0, 0.0, 0.0);
+
+  double eps = 1.0e-3;
+  vector<Vector3D> cneg, cpos, czero;
+  double k1min = std::numeric_limits<double>::max();
+  double k1max = std::numeric_limits<double>::lowest();
+  double k2min = std::numeric_limits<double>::max();
+  double k2max = std::numeric_limits<double>::lowest();
+  int k1pos = 0, k1neg = 0, k2pos = 0, k2neg = 0;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      double kmin = group_points_[kr]->minPrincipalCurvature();
+      double kmax = group_points_[kr]->maxPrincipalCurvature();
+      d1 += kmin;
+      d2 += kmax;
+      k1_1 += kmin*kmin;
+      k2_1 += kmax*kmax;
+      k1_2 += fabs(kmin);
+      k2_2 += fabs(kmax);
+      k1mean += wgt*kmin;
+      k2mean += wgt*kmax;
+      k1min = std::min(k1min, kmin);
+      k1max = std::max(k1max, kmin);
+      k2min = std::min(k2min, kmax);
+      k2max = std::max(k2max, kmax);
+      if (kmin < 0.0)
+	k1neg++;
+      else
+	k1pos++;
+      if (kmax < 0.0)
+	k2neg++;
+      else
+	k2pos++;
+
+      Point norm = group_points_[kr]->getLocFuncNormal();
+      Point minvec = group_points_[kr]->minCurvatureVec();
+      Point temp = norm.cross(minvec);
+      temp.normalize_checked();
+      vec += wgt*temp;
+      if (group_points_[kr]->GaussCurvature()*group_points_[kr]->meanCurvature() < -eps)
+	cneg.push_back(group_points_[kr]->getPoint());
+      else if (group_points_[kr]->GaussCurvature()*group_points_[kr]->meanCurvature() > eps)
+	cpos.push_back(group_points_[kr]->getPoint());
+      else
+	czero.push_back(group_points_[kr]->getPoint());
+    }
+  //double vark1 = ((double)group_points_.size()*k1_1)/fabs(d1) - fabs(k1_2);
+  //double vark2 = ((double)group_points_.size()*k2_1)/fabs(d2) - fabs(k2_2);
+
+  double klim1 = 0.1*fabs(k1mean);
+  double klim2 = 0.1*fabs(k2mean);
+  int nmb1=0, nmb2=0;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      double kmin = group_points_[kr]->minPrincipalCurvature();
+      double kmax = group_points_[kr]->maxPrincipalCurvature();
+      if (fabs(kmin - k1mean) < klim1)
+	++nmb1;
+     if (fabs(kmax - k2mean) < klim2)
+	++nmb2;
+    }
+
+  //double frac1 = (double)nmb1/(double)group_points_.size();
+  //double frac2 = (double)nmb2/(double)group_points_.size();
+
+#ifdef DEBUG_EXTRACT
+  std::ofstream of("posnegcurv.g2");
+  of << "400 1 0 4 0 100 155 255" << std::endl;
+  of << cneg.size() << std::endl;
+  for (size_t kr=0; kr<cneg.size(); ++kr)
+    of << cneg[kr] << std::endl;
+  of << "400 1 0 4 0 255 0 255" << std::endl;
+  of << cpos.size() << std::endl;
+  for (size_t kr=0; kr<cpos.size(); ++kr)
+    of << cpos[kr] << std::endl;
+  of << "400 1 0 4 200 55 0 255" << std::endl;
+  of << czero.size() << std::endl;
+  for (size_t kr=0; kr<czero.size(); ++kr)
+    of << czero[kr] << std::endl;
+#endif
+  // Something with variation in curvature
+  return true;  // For the time being
+}
+
+//===========================================================================
+void RevEngRegion::analysePlaneProperties(Point avnorm, double angtol,
+					  vector<RevEngPoint*>& in,
+					  vector<RevEngPoint*> out)
+//===========================================================================
+{
+  vector<double> midang(group_points_.size());
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point curr = group_points_[ki]->getLocFuncNormal();
+      double ang = curr.angle(avnorm);
+      midang[ki] = ang;
+      if (ang <= angtol)
+	in.push_back(group_points_[ki]);
+      else
+	out.push_back(group_points_[ki]);
+    }
+
+  std::sort(midang.begin(), midang.end());
+  int stop_break = 1;
+}
+
+//===========================================================================
+void RevEngRegion::analyseNormals(double tol, Point& normal, Point& centre,
+				  double& radius) //double& beta)
+//===========================================================================
+{
+#ifdef DEBUG0
+  std::ofstream of2("curr_normals.g2");
+  of2 << "400 1 0 4 100  0 155 255" << std::endl;
+  of2 << group_points_.size() << std::endl;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(group_points_[kr]);
+      Point norm = pt->getLocFuncNormal();
+      of2 << norm << std::endl;
+    }
+  Sphere sph(1.0, Point(0.0, 0.0, 0.0), Point(0.0, 0.0, 1.0),
+	     Point(1.0, 0.0, 0.0));
+  sph.writeStandardHeader(of2);
+  sph.write(of2);
+#endif
+  
+  Point axis;
+  Point Cx;
+  Point Cy;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  RevEngUtils::computeAxis(group, axis, Cx, Cy);
+
+  Point pnt(0.0, 0.0, 0.0);
+  Point pos(0.0, 0.0, 0.0);
+  vector<Point> vec(group_points_.size());
+  double wgt = 1.0/(double)(group_points_.size());
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector3D xyz = group_points_[ki]->getPoint();
+      pos += wgt*Point(xyz[0], xyz[1], xyz[2]);
+      vec[ki] = group_points_[ki]->getLocFuncNormal();
+      pnt += wgt*vec[ki];
+    }
+  Point tmpnorm;
+  double maxang = 0.0;
+  size_t ix = 0;
+  for (size_t kj=1; kj<vec.size(); ++kj)
+    {
+      double ang = vec[0].angle(vec[kj]);
+      ang = std::min(ang, M_PI-ang);
+      if (ang > maxang)
+	{
+	  maxang = ang;
+	  ix = kj;
+	}
+    }
+  tmpnorm = vec[0].cross(vec[ix]);
+  tmpnorm.normalize();
+
+  ImplicitApprox impl;
+  impl.approxPoints(vec, 1);
+
+  //Point pos; //, normal;
+  bool found = impl.projectPoint(pos, tmpnorm, pnt, normal);
+  if (!found)
+    {
+      pnt = pos;
+      normal = tmpnorm;
+    }
+  shared_ptr<Plane> surf(new Plane(pnt, axis));
+
+#ifdef DEBUG0
+  Point origo(0.0, 0.0,0.0);
+  of2 << "410 1 0 4 255 0 0 255" << std::endl;
+  of2 << "1" << std::endl;
+  of2 << origo << " " << axis << std::endl;
+#endif
+  
+  double maxdist, avdist;
+  int num_inside;
+  //int num = (int)vec.size();
+  vector<double> distance;
+  RevEngUtils::distToSurf(vec, surf, tol, maxdist, avdist, num_inside,
+			  distance);
+
+  //double radius;
+  RevEngUtils::computeRadius(vec, axis, Cx, Cy, radius);
+  centre = pnt;
+  // double r2 = std::min(radius, 1.0);
+  // double phi = acos(r2);
+  // double delta = r2*tan(phi);
+  // double alpha = (delta > 1.0e-10) ? atan((1.0-r2)/delta) : M_PI;
+  // beta = M_PI - alpha;
+  int stop_break = 1;
+
+}
+
+//===========================================================================
+bool RevEngRegion::feasiblePlane(double zero_H, double zero_K) const
+//===========================================================================
+{
+  double angtol = 0.2;
+  double in_lim = 0.8;
+  double in_lim2 = 0.5;
+  double fac = 5.0;
+  if (basesf_ && basesf_->instanceType() == Class_Plane)
+    return true;
+
+  if (normalcone_.angle() < angtol && (!normalcone_.greaterThanPi()))
+    return true;
+
+  int nmb_in = 0;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      Point normal1 = group_points_[kr]->getLocFuncNormal();
+      Point normal2 = group_points_[kr]->getTriangNormal();
+      if (avnorm_.angle(normal1) <= angtol ||
+	  avnorm2_.angle(normal2) <= angtol)
+	nmb_in++;
+    }
+  double in_frac = (double)nmb_in/(double)group_points_.size();
+  if (in_frac > in_lim)
+    return true;
+
+  if (in_frac < in_lim2)
+    return false;
+  
+  if (std::max(MAH_, MAK_) > fac*std::max(zero_H, zero_K))
+    return false;
+
+  return true;
+}
+
+//===========================================================================
+bool RevEngRegion::feasibleCylinder(double zero_H, double zero_K) const
+//===========================================================================
+{
+  double fac1 = 5.0;
+  double fac2 = 0.5;
+  if (basesf_ && basesf_->instanceType() == Class_Cylinder)
+    return true;
+  if (MAK_ > fac1*zero_H)
+    return false;
+  if (MAK_ > fac2*MAH_)
+    return false;
+  // if (fabs(avK_) < fac2*MAK_)
+  //   return false;
+
+  return true;
+}
+
+
+//===========================================================================
+bool RevEngRegion::extractCylinder(double tol, int min_pt, int min_pt_reg,
+				   double angtol, int prefer_elementary,
+				   vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				   vector<HedgeSurface*>& prevsfs,
+				   vector<vector<RevEngPoint*> >& out_groups,
+				   bool& repeat)
+//===========================================================================
+{
+  bool found = false;
+  int min_nmb = 20;
+  //double eps = 1.0e-6;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+
+  vector<vector<RevEngPoint*> > configs;
+  shared_ptr<Cone> cone;
+  shared_ptr<Cylinder> cyl = computeCylinder(group_points_, tol);
+#ifdef DEBUG_CYL
+  std::ofstream ofs("cyl.g2");
+  shared_ptr<Cylinder> cyl2(cyl->clone());
+  double diag = bbox_.low().dist(bbox_.high());
+  cyl2->setParamBoundsV(-0.5*diag,0.5*diag);
+  cyl2->writeStandardHeader(ofs);
+  cyl2->write(ofs);
+#endif
+  
+  // Check accuracy
+  double maxd, avd; 
+  int num2, num2_2; 
+  vector<RevEngPoint*> inpt, outpt; 
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  cyl, tol, maxd, avd, num2, num2_2, inpt, outpt, 
+			  parvals, dist_ang, angtol);
+
+#ifdef DEBUG_CYL
+  std::ofstream ofd("in_out_cyl.g2");
+  ofd << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd << inpt.size() << std::endl;
+  for (size_t kr=0; kr<inpt.size(); ++kr)
+    ofd << inpt[kr]->getPoint() << std::endl;
+  ofd << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd << outpt.size() << std::endl;
+  for (size_t kr=0; kr<outpt.size(); ++kr)
+    ofd << outpt[kr]->getPoint() << std::endl;
+#endif
+
+  int num_in_lin, num_in_cub;
+  double avd_lin, avd_cub;
+  analyseCylRotate(cyl, tol, avd, num2, avd_lin, num_in_lin,
+		   avd_cub, num_in_cub, cone);
+  analyseCylProject(cyl, tol, configs);
+  if (cone.get())
+    {
+      double maxd2, avd2;
+      int num3, num3_2;
+      vector<RevEngPoint*> inpt2, outpt2;
+      vector<pair<double, double> > dist_ang2;
+      vector<double> parvals2;
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      cone, tol, maxd2, avd2, num3, num3_2, inpt2, outpt2,
+			      parvals2, dist_ang2, angtol);
+#ifdef DEBUG_CYL
+      std::ofstream ofsc("cone_cyl.g2");
+      shared_ptr<Cone> cone2(cone->clone());
+      double diag = bbox_.low().dist(bbox_.high());
+      cone2->setParamBoundsV(-0.5*diag,0.5*diag);
+      cone2->writeStandardHeader(ofsc);
+      cone2->write(ofsc);
+      std::ofstream ofdc("in_out_cone_cyl.g2");
+      ofdc << "400 1 0 4 155 50 50 255" << std::endl;
+      ofdc << inpt2.size() << std::endl;
+      for (size_t kr=0; kr<inpt2.size(); ++kr)
+	ofdc << inpt2[kr]->getPoint() << std::endl;
+      ofdc << "400 1 0 4 50 155 50 255" << std::endl;
+      ofdc << outpt.size() << std::endl;
+      for (size_t kr=0; kr<outpt2.size(); ++kr)
+	ofdc << outpt2[kr]->getPoint() << std::endl;
+#endif
+      int stop_cone = 1;
+    }
+  
+  if (configs.size() > 1 && (!hasSurface()))
+    {
+      // Split point group and try again
+      int keep_ix = -1;
+      int keep_nmb = 0;
+      for (size_t ki=0; ki<configs.size(); ++ki)
+	if ((int)configs[ki].size() > keep_nmb)
+	  {
+	    keep_nmb = (int)configs[ki].size();
+	    keep_ix = (int)ki;
+	  }
+      
+      for (size_t ki=0; ki<configs.size(); ++ki)
+	{
+	  if ((int)ki == keep_ix)
+	    continue;
+
+	  extractSpesPoints(configs[ki], out_groups);
+	}
+
+      // Check that the remaing point cloud is connected
+      splitRegion(out_groups);
+
+      if (hasSurface())
+	{
+	  for (size_t ki=0; ki<associated_sf_.size(); ++ki)
+	    prevsfs.push_back(associated_sf_[ki]);
+	  clearSurface();
+	}
+      
+      updateInfo(tol, angtol);
+      repeat = true;
+    }
+  else
+    {
+      int num = (int)group_points_.size();
+      if (num2 > min_pt && num2 > num/2)
+	{
+	  // Check for deviant points at the boundary
+	  vector<RevEngPoint*> dist_points;
+	  identifyDistPoints(dist_ang, tol, maxd, avd, dist_points);
+	  extractSpesPoints(dist_points, out_groups, true);
+	  if (out_groups.size() > 0)
+	    {
+	      // Ensure connected region
+	      vector<vector<RevEngPoint*> > separate_groups;
+	      splitRegion(separate_groups);
+	      if (separate_groups.size() > 0)
+		{
+		  out_groups.insert(out_groups.end(), separate_groups.begin(),
+				    separate_groups.end());
+		}
+	  
+	      shared_ptr<Cylinder> cyl_in = computeCylinder(group_points_, tol);
+	      vector<RevEngPoint*> inpt_in, outpt_in; 
+	      vector<pair<double, double> > dist_ang_in;
+	      vector<double> parvals_in;
+	      double maxd_in, avd_in;
+	      int num2_in, num2_in2;
+	      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+				      cyl_in, tol, maxd_in, avd_in, num2_in, num2_in2,
+				      inpt_in, outpt_in, parvals_in, dist_ang_in, angtol);
+
+	      
+#ifdef DEBUG_CYL
+	      std::ofstream ofd2("in_out_cylinder2.g2");
+	      ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+	      ofd2 << inpt_in.size() << std::endl;
+	      for (size_t kr=0; kr<inpt_in.size(); ++kr)
+		ofd2 << inpt_in[kr]->getPoint() << std::endl;
+	      ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+	      ofd2 << outpt_in.size() << std::endl;
+	      for (size_t kr=0; kr<outpt_in.size(); ++kr)
+		ofd2 << outpt_in[kr]->getPoint() << std::endl;
+#endif
+	      std::swap(cyl, cyl_in);
+	      std::swap(num2, num2_in);
+	      std::swap(num2_2, num2_in2);
+	      std::swap(avd, avd_in);
+	      std::swap(maxd, maxd_in);
+	      std::swap(parvals, parvals_in);
+	      std::swap(dist_ang, dist_ang_in);
+	    }
+	}
+	  
+      double maxd_init, avd_init;
+      int num_init, num_init2;
+      getAccuracy(maxd_init, avd_init, num_init, num_init2);
+      int sf_flag = defineSfFlag(0, tol, num2, num2_2,
+				 avd, true);
+      if (sf_flag < ACCURACY_POOR)
+	{
+	  bool OK = true;
+	  double acc_fac = 1.5;
+	  if (associated_sf_.size() > 0)
+	    {
+	      int sfcode;
+	      int sftype = associated_sf_[0]->instanceType(sfcode);
+	      double ang = (sftype == Class_Plane) ?
+		normalcone_.angle() : M_PI;
+	      double ang_lim = 0.1*M_PI;
+	      
+	      // Check with current approximating surface
+	      if (surfflag_ == ACCURACY_OK && sf_flag > surfflag_)
+		OK = false;
+	      else if (prefer_elementary == ALWAYS_ELEM ||
+		       prefer_elementary == PREFER_ELEM)
+		{
+		  if (!(ang > ang_lim && (num2 < num_init ||
+					  (avd < avd_init &&
+					   num2 < acc_fac*num_init))))
+		    OK = false;
+		}
+	      else
+		{
+		  if (!(num2 < num_init ||
+			(avd < avd_init && num2 < acc_fac*num_init)))
+		    OK = true;
+		}
+	      if (sf_flag == ACCURACY_OK && surfflag_ > ACCURACY_OK)
+		OK = true;
+	    }
+
+	  if (OK)
+	    {
+	      found = true;
+	      for (size_t kh=0; kh<group_points_.size(); ++kh)
+		{
+		  group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+		  group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+		}
+	      setAccuracy(maxd, avd, num2, num2_2);
+      
+	      // // Limit cylinder with respect to bounding box
+	      // double gap = 1.0e-6;
+	      // Point xdir(1.0, 0.0, 0.0);
+	      // Point ydir(0.0, 1.0, 0.0);
+	      // Point zdir(0.0, 0.0, 1.0);
+	      // Point bbdiag = high - low;
+	      // CompositeModelFactory factory(gap, gap, 10.0*gap, 0.01, 0.05);
+	      // shared_ptr<SurfaceModel> boxmod(factory.createFromBox(low-2.0*bbdiag, xdir, ydir, 
+	      // 							    5*bbdiag[0], 5*bbdiag[1],
+	      // 							    5*bbdiag[2]));
+	      // vector<shared_ptr<ParamSurface> > sfs;
+	      // sfs.push_back(cyl);
+	      // shared_ptr<SurfaceModel> cylmod(new SurfaceModel(gap, gap, 10.0*gap, 0.01,
+	      // 						       0.05, sfs));
+	      // vector<shared_ptr<SurfaceModel> > divcyl = cylmod->splitSurfaceModels(boxmod);
+
+#ifdef DEBUG
+	      std::cout << "Cylinder. N1: " << num << ", N2: " << num2 << ", max: " << maxd << ", av: " << avd << std::endl;
+#endif
+	      shared_ptr<HedgeSurface> hedge(new HedgeSurface(cyl, this));
+	      for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+		prevsfs.push_back(associated_sf_[kh]);
+	      setHedge(hedge.get());
+	      hedgesfs.push_back(hedge);
+	      setSurfaceFlag(sf_flag);
+	      // for (int ka=0; ka<divcyl[0]->nmbEntities(); ++ka)
+	      // 	{
+	      // 	  shared_ptr<ParamSurface> cyl2 = divcyl[0]->getSurface(ka);
+	      // 	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(cyl2, this));
+	      // 	  for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+	      // 	    prevsfs.push_back(associated_sf_[kh]);
+	      // 	  associated_sf_.push_back(hedge.get());
+	      // 	  hedgesfs.push_back(hedge);
+	  
+	    }
+	}
+    }
+  if (!basesf_.get() ||
+      (num2 >= num_in_base_ && avd < avdist_base_))
+    setBaseSf(cyl, maxd, avd, num2, num2_2);
+
+  return found;
+}
+
+//===========================================================================
+shared_ptr<Cylinder>
+RevEngRegion::computeCylinder(vector<RevEngPoint*>& points, double tol)
+//===========================================================================
+{
+  // Cylinder orientation by covariance matrix of normal vectors
+  Point axis;
+  Point Cx;
+  Point Cy;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  RevEngUtils::computeAxis(group, axis, Cx, Cy);
+  
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  //double len = low.dist(high);
+  double rad;
+  Point pnt;
+  RevEngUtils::computeCylPosRadius(group, low, high,
+				   axis, Cx, Cy, pnt, rad);
+  shared_ptr<Cylinder> cyl(new Cylinder(rad, pnt, axis, Cy));
+#ifdef DEBUG_CYL
+  std::ofstream of("cylinder_compute.g2");
+  cyl->writeStandardHeader(of);
+  cyl->write(of);
+#endif
+  return cyl;
+}
+
+//===========================================================================
+void RevEngRegion::analyseCylProject(shared_ptr<Cylinder> cyl, double tol,
+				     vector<vector<RevEngPoint*> >& configs)
+//===========================================================================
+{
+#ifdef DEBUG_CYL
+  std::ofstream of("projected_pts_cyl.g2");
+#endif
+  
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  Point pnt = cyl->location();
+  Point axis, Cx, Cy;
+  cyl->getCoordinateAxes(Cx, Cy, axis);
+  double rad = cyl->getRadius();
+  vector<Point> projected;
+  double maxdp, avdp;
+  RevEngUtils::projectToPlane(group, axis, pnt, projected, maxdp, avdp);
+  shared_ptr<Circle> circ(new Circle(rad, pnt, axis, Cx));
+  shared_ptr<SplineCurve> spl;
+  Point xpos;
+  vector<double> param;
+  curveApprox(projected, tol, circ, param, spl, xpos);
+  
+#ifdef DEBUG_CYL
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << projected.size() << std::endl;
+  for (size_t kr=0; kr<projected.size(); ++kr)
+    of << projected[kr] << std::endl;
+   circ->writeStandardHeader(of);
+  circ->write(of);
+  spl->writeStandardHeader(of);
+  spl->write(of);
+#endif
+  
+  double maxdc, avdc, maxds, avds;
+  int num_inc, num_ins;
+  RevEngUtils::distToCurve(projected, circ, tol, maxdc, avdc, num_inc);
+  RevEngUtils::distToCurve(projected, spl, tol, maxds, avds, num_ins);
+
+  double len = bbox_.low().dist(bbox_.high());
+  if (maxds < maxdc && num_ins > num_inc && 
+      num_ins > (int)group_points_.size()/4 && rad < 2.0*len)
+    {
+      // Investigate point configuration
+      configSplit(group_points_, param, cyl, spl, maxds, configs);
+#ifdef DEBUG_CYL
+      std::ofstream ofconf("conf_groups.g2");
+      for (size_t ki=0; ki<configs.size(); ++ki)
+	{
+	  ofconf << "400 1 0 0" << std::endl;
+	  ofconf << configs[ki].size() << std::endl;
+	  for (size_t kr=0; kr<configs[ki].size(); ++kr)
+	    ofconf << configs[ki][kr]->getPoint() << std::endl;
+	}
+#endif
+    }
+  
+  if (configs.size() <= 1 && (num_ins >= num_inc && avds <= avdc) &&
+      (num_ins > (int)group_points_.size()/2 && avds < tol) &&
+      (!sweep_.get() || sweep_->type_ != 1 ||
+       (sweep_->num_in_ < num_ins && sweep_->avdist_ > avds)))
+    {
+      // Possible linear sweep
+      Point pt1 = pnt - len*axis; 
+      Point pt2 = pnt + len*axis; 
+      sweep_ = shared_ptr<SweepData>(new SweepData(1, spl, pt1, pt2, maxds, avds, num_ins));
+    }
+  int stop_break = 1;
+ }
+
+
+//===========================================================================
+bool RevEngRegion::defineConeFromCyl(shared_ptr<Cylinder> cyl, double tol,
+				     double angtol, int min_pt_reg,
+				     double avdist, int num_in, int num2_in,
+				     vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				     vector<vector<RevEngPoint*> >& out_groups,
+				     vector<RevEngPoint*>& single_pts)
+//===========================================================================
+{
+#ifdef DEBUG_CYL
+  std::ofstream of("rotated_pts.g2");
+#endif
+  
+  Point pnt = cyl->location();
+  Point axis, Cx, Cy;
+  cyl->getCoordinateAxes(Cx, Cy, axis);
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  double len = bbox_.low().dist(bbox_.high());
+  RevEngUtils::rotateToPlane(group, Cy, axis, pnt, rotated);
+  shared_ptr<Line> line(new Line(pnt, axis));
+  line->setParameterInterval(-len, len);
+  Point pt1 = line->ParamCurve::point(-len);
+  Point pt2 = line->ParamCurve::point(len);
+  shared_ptr<SplineCurve> line_cv(new SplineCurve(pt1, -len, pt2, len));
+  shared_ptr<SplineCurve> crv;
+  RevEngUtils::curveApprox(rotated, line_cv, 2, 2, crv);
+  double maxdcrv, avdcrv;
+  int num_in_crv;
+  vector<double> dist;
+  RevEngUtils::distToCurve(rotated, crv, tol, maxdcrv, avdcrv, num_in_crv, dist);
+#ifdef DEBUG_CYL
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << rotated.size() << std::endl;
+  for (size_t kr=0; kr<rotated.size(); ++kr)
+    of << rotated[kr] << std::endl;
+
+  crv->writeStandardHeader(of);
+  crv->write(of);
+#endif
+
+  if (avdcrv > avdist || num_in_crv < num2_in)
+    return false; // No gain in replacing the cylinder with a cone
+
+  // Identify distant points
+  double dlim = std::max(2.0*tol, 2.0*avdcrv);
+  vector<RevEngPoint*> dist_pts;
+  vector<RevEngPoint*> remaining;
+  vector<Point> rotated2;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      if (dist[ki] > dlim)
+	dist_pts.push_back(group_points_[ki]);
+      else
+	{
+	  remaining.push_back(group_points_[ki]);
+	  rotated2.push_back(rotated[ki]);
+	}
+    }
+#ifdef DEBUG_CYL
+  std::ofstream of3("split_group.g2");
+  if (remaining.size() > 0)
+    {
+      of3 << "400 1 0 4 255 0 0 255" << std::endl;
+      of3 << remaining.size() << std::endl;
+      for (size_t kr=0; kr<remaining.size(); ++kr)
+	of3 << remaining[kr]->getPoint() << std::endl;
+    }
+  if (dist_pts.size() > 0)
+    {
+      of3 << "400 1 0 4 0 255 0 255" << std::endl;
+      of3 << dist_pts.size() << std::endl;
+      for (size_t kr=0; kr<dist_pts.size(); ++kr)
+	of3 << dist_pts[kr]->getPoint() << std::endl;
+    }
+#endif
+  if (remaining.size() < 0.5*group_points_.size())
+    return false;
+  
+  // Define cone
+  shared_ptr<SplineCurve> crv2;
+  RevEngUtils::curveApprox(rotated2, line_cv, 2, 2, crv2);
+  double tclose, dclose;
+  Point ptclose;
+  crv2->closestPoint(pnt, crv2->startparam(), crv2->endparam(), tclose,
+		     ptclose, dclose);
+  vector<Point> der(2);
+  crv2->point(der, tclose, 1);
+  double phi = der[1].angle(axis);
+
+  // Check sign of angle
+  Point pnt2 = pnt + 0.1*len*axis;
+  double tclose2, dclose2;
+  Point ptclose2;
+  crv2->closestPoint(pnt2, crv2->startparam(), crv2->endparam(), tclose2,
+		     ptclose2, dclose2);
+  if (dclose2 < dclose)
+    phi *= -1;
+      
+  shared_ptr<Cone> cone(new Cone(dclose, pnt, axis, Cy, phi));
+#ifdef DEBUG_CYL
+  std::ofstream of2("cone_from_rotate.g2");
+  double t1 = crv2->startparam();
+  double t2 = crv2->endparam();
+  cone->setParamBoundsV(t1-0.1*(t2-t1), t2+0.1*(t2-t1));
+  cone->writeStandardHeader(of2);
+  cone->write(of2);
+#endif
+
+  // Check accuracy
+  bool found = false;
+  double maxdistco, avdistco;
+  int num_insideco, num2_insideco;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  vector<RevEngPoint*> inco, outco;
+  RevEngUtils::distToSurf(remaining.begin(), remaining.end(),
+			  cone, tol, maxdistco, avdistco,
+			  num_insideco, num2_insideco,
+			  inco, outco, parvals, dist_ang, angtol);
+  int sf_flag = defineSfFlag(remaining.size(), 0, tol, num_insideco, 
+			     num2_insideco, avdistco, true);
+  if (sf_flag < ACCURACY_POOR)
+    {
+      bool OK = true;
+      double acc_fac = 1.5;
+      double frac = (double)remaining.size()/(double)group_points_.size();
+      if (associated_sf_.size() > 0)
+	{
+	  double maxd_init, avd_init;
+	  int num_init, num_init2;
+	  getAccuracy(maxd_init, avd_init, num_init, num_init2);
+	  if (surfflag_ == ACCURACY_OK && sf_flag > surfflag_)
+	    OK = false;
+	  else
+	    {
+	      if (!((double)num_insideco < frac*(double)num_init ||
+		    (avdistco < avd_init &&
+		     (double)num_insideco < acc_fac*frac*(double)num_init)))
+		OK = true;
+	    }
+	  if (sf_flag == ACCURACY_OK && surfflag_ > ACCURACY_OK)
+	    OK = true;
+	}
+
+      if (OK)
+	{
+	  found = true;
+	  std::swap(group_points_, remaining);
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	    }
+	  setAccuracy(maxdistco, avdistco, num_insideco, num2_insideco);
+	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(cone, this));
+
+	  setHedge(hedge.get());
+	  hedgesfs.push_back(hedge);
+	  setSurfaceFlag(sf_flag);
+	  
+	  // Make sure that the distant points are connected
+	  vector<vector<RevEngPoint*> > separate_groups;
+	  vector<RevEngPoint*> dummy;
+	  connectedGroups(dist_pts, separate_groups, false, dummy);
+	  if (separate_groups.size() > 0)
+	    {
+	      for (size_t kh=0; kh<separate_groups.size(); ++kh)
+		{
+#ifdef DEBUG_CYL
+		  std::ofstream of4("curr_sep_group.g2");
+		  of4 << "400 1 0 4 0 0 255 255" << std::endl;
+		  of4 << separate_groups[kh].size() << std::endl;
+		  for (size_t kr=0; kr<separate_groups[kh].size(); ++kr)
+		    of4 << separate_groups[kh][kr]->getPoint() << std::endl;
+#endif
+		  // Check if the group can be added to the current region given
+		  // the updated cone
+		  vector<RevEngPoint*> in2, out2;
+		  vector<pair<double,double> > dist_ang2;
+		  vector<double> parvals2;
+		  int nmb_in2, nmb2_in2;
+		  double maxd2, avd2;
+		  RevEngUtils::distToSurf(separate_groups[kh].begin(),
+					  separate_groups[kh].end(),
+					  cone, tol, maxd2, avd2, nmb_in2, nmb2_in2,
+					  in2, out2, parvals2, dist_ang2, angtol);
+		  int sf_flag2 = defineSfFlag(separate_groups[kh].size(), 0, tol,
+					      nmb_in2, nmb2_in2, avd2, true);
+
+		  if (sf_flag2 <= sf_flag)
+		    {
+		      for (size_t kr=0; kr<separate_groups[kh].size(); ++kr)
+			{
+			  separate_groups[kh][kr]->setPar(Vector2D(parvals2[2*kr],
+								   parvals2[2*kr+1]));
+			  separate_groups[kh][kr]->setSurfaceDist(dist_ang2[kr].first,
+								  dist_ang2[kr].second);
+			}
+		      (void)addPointsToGroup(separate_groups[kh], tol, angtol);
+		    }
+		  else if (separate_groups[kh].size() == 1)
+		    {
+		      separate_groups[kh][0]->unsetRegion();
+		      single_pts.push_back(separate_groups[kh][0]);
+		    }
+		  else
+		    {
+		      for (size_t kr=0; kr<separate_groups[kh].size(); ++kr)
+			separate_groups[kh][kr]->unsetRegion();
+		      out_groups.push_back(separate_groups[kh]);
+		    }
+		}
+	    }
+	}
+    }
+  return found;
+ }
+  
+//===========================================================================
+void RevEngRegion::analyseCylRotate(shared_ptr<Cylinder> cyl, double tol,
+				    double avdist, int num_in,
+				    double& avdist_lin, int& num_in_lin,
+				    double& avdist_cub, int& num_in_cub,
+				    shared_ptr<Cone>& cone)
+//===========================================================================
+{
+#ifdef DEBUG_CYL
+  std::ofstream of("rotated_pts_cyl.g2");
+#endif
+  
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  Point pnt = cyl->location();
+  Point axis, Cx, Cy;
+  cyl->getCoordinateAxes(Cx, Cy, axis);
+  //double rad = cyl->getRadius();
+  vector<Point> rotated;
+  RevEngUtils::rotateToPlane(group, Cy, axis, pnt, rotated);
+  double len = bbox_.low().dist(bbox_.high());
+#ifdef DEBUG_CYL
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << rotated.size() << std::endl;
+  for (size_t kr=0; kr<rotated.size(); ++kr)
+    of << rotated[kr] << std::endl;
+  of << "410 1 0 4 0 0 255 255" << std::endl;
+  of << "1" << std::endl;
+  of << pnt-0.5*len*axis << " " << pnt+0.5*len*axis << std::endl;
+  of << "410 1 0 4 0 255 0 255" << std::endl;
+  of << "1" << std::endl;
+  of << pnt-0.5*len*Cy << " " << pnt+0.5*len*Cy << std::endl;
+#endif
+
+  shared_ptr<Line> line(new Line(pnt, axis));
+  line->setParameterInterval(-len, len);
+  shared_ptr<SplineCurve> line2;
+  RevEngUtils::curveApprox(rotated, line, 2, 2, line2);
+  vector<Point> der(2);
+  line2->point(der, 0.0, 1);
+
+  Point pt1 = line->ParamCurve::point(-len);
+  Point pt2 = line->ParamCurve::point(len);
+  shared_ptr<SplineCurve> line_cv(new SplineCurve(pt1, -len, pt2, len));
+  shared_ptr<SplineCurve> cv1, cv2;
+  RevEngUtils::curveApprox(rotated, line_cv, 2, 2, cv1);
+  RevEngUtils::curveApprox(rotated, line_cv, 4, 8, cv2);
+  double maxdcv1, maxdcv2;
+  RevEngUtils::distToCurve(rotated, cv1, tol, maxdcv1, avdist_lin, num_in_lin);
+  RevEngUtils::distToCurve(rotated, cv2, tol, maxdcv2, avdist_cub, num_in_cub);
+#ifdef DEBUG_CYL
+  cv1->writeStandardHeader(of);
+  cv1->write(of);
+  cv2->writeStandardHeader(of);
+  cv2->write(of);
+#endif
+  
+  // Check if a cone is feasible
+  double cfac = 1.2;
+  if (avdist_lin < cfac*std::min(avdist_cub, avdist) &&
+      cfac*num_in_lin > (double)std::max(num_in_cub, num_in))
+    {
+      double tclose, dclose;
+      Point ptclose;
+      cv1->closestPoint(pnt, cv1->startparam(), cv1->endparam(), tclose,
+			ptclose, dclose);
+      vector<Point> der(2);
+      cv1->point(der, tclose, 1);
+      double phi = der[1].angle(axis);
+
+      // Check sign of angle
+      Point pnt2 = pnt + 0.1*len*axis;
+      double tclose2, dclose2;
+      Point ptclose2;
+      cv1->closestPoint(pnt2, cv1->startparam(), cv1->endparam(), tclose2,
+			ptclose2, dclose2);
+      if (dclose2 < dclose)
+	phi *= -1;
+      
+      cone = shared_ptr<Cone>(new Cone(dclose, pnt, axis, Cy, phi));
+#ifdef DEBUG_CYL
+      std::ofstream of2("cone_from_rotate.g2");
+      double t1 = cv1->startparam();
+      double t2 = cv1->endparam();
+      cone->setParamBoundsV(t1-0.1*(t2-t1), t2+0.1*(t2-t1));
+      cone->writeStandardHeader(of2);
+      cone->write(of2);
+#endif
+    }
+
+  int stop_break = 1;
+}
+
+
+
+//===========================================================================
+bool RevEngRegion::extractLinearSweep(double tol, int min_pt, int min_pt_reg,
+				      double angtol, int prefer_elementary,
+				      std::vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				      std::vector<HedgeSurface*>& prevsfs)
+//===========================================================================
+{
+  if (!sweep_.get())
+    return false;
+
+  if (sweep_->type_ != 1)
+    return false;
+  
+  bool found = false;
+  shared_ptr<SplineSurface> surf;
+
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+  
+  SweepSurfaceCreator sweep;
+  shared_ptr<SplineCurve> along(new SplineCurve(sweep_->location_, sweep_->added_info_));
+  Point mid = 0.5*(sweep_->location_ + sweep_->added_info_);
+  surf = shared_ptr<SplineSurface>(sweep.linearSweptSurface(*along,
+							  *sweep_->profile_, mid));
+  if (!surf.get())
+    return false;
+#ifdef DEBUG_EXTRACT
+  std::ofstream of3("sweep.g2");
+  along->writeStandardHeader(of3);
+  along->write(of3);
+  sweep_->profile_->writeStandardHeader(of3);
+  sweep_->profile_->write(of3);
+  surf->writeStandardHeader(of3);
+  surf->write(of3);
+  of3 << "400 1 0 4 255 0 0 255" << std::endl;
+  of3 << "1" << std::endl;
+  of3 << sweep_->location_ << std::endl;
+#endif
+  
+  // Check accuracy
+  double maxd, avd; 
+  int num2, num2_2; 
+  vector<RevEngPoint*> inpt, outpt;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+  			  surf, tol, maxd, avd, num2, num2_2, inpt, outpt,
+			  parvals, dist_ang, angtol);
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofd("in_out_surf.g2");
+  ofd << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd << inpt.size() << std::endl;
+  for (size_t kr=0; kr<inpt.size(); ++kr)
+    ofd << inpt[kr]->getPoint() << std::endl;
+  ofd << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd << outpt.size() << std::endl;
+  for (size_t kr=0; kr<outpt.size(); ++kr)
+    ofd << outpt[kr]->getPoint() << std::endl;
+#endif
+  //int num = (int)group_points_.size();
+  double maxd_init, avd_init;
+  int num_init, num_init2;
+  getAccuracy(maxd_init, avd_init, num_init, num_init2);
+  int sf_flag = defineSfFlag(0, tol, num2, num2_2, avd, false);
+  if (sf_flag < ACCURACY_POOR)
+    {
+     bool OK = true;
+      if (associated_sf_.size() > 0)
+	{
+	  // Check with current approximating surface
+	  double acc_fac1 = 1.25;
+	  double acc_fac2 = 0.75;
+	  if (surfflag_ == ACCURACY_OK && sf_flag > surfflag_)
+	    OK = false;
+	  else if (prefer_elementary == ALWAYS_ELEM)
+	    OK = false;
+	  else if (prefer_elementary == PREFER_ELEM &&
+		   ((double)num2 < acc_fac1*num_init ||
+		    avd > acc_fac2*avd_init))
+	    OK = false;
+	  else if (prefer_elementary == BEST_ACCURACY &&
+		   (num2 < num_init || avd > avd_init))
+	    OK = false;
+	}
+
+      if (OK)
+	{
+	  found = true;
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	    }
+	  setAccuracy(maxd, avd, num2, num2_2);
+#ifdef DEBUG
+	  std::cout << "Linear swept surface. N1: " << num << ", N2: " << num2 << ", max: " << maxd << ", av: " << avd << std::endl;
+#endif
+	  
+	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(surf, this));
+	  hedge->setLinearSweepInfo(sweep_->profile_, sweep_->location_, sweep_->added_info_);
+	  for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+	    prevsfs.push_back(associated_sf_[kh]);
+	  setHedge(hedge.get());
+	  hedgesfs.push_back(hedge);
+	  setSurfaceFlag(sf_flag);
+	}
+    }
+
+   return found;
+ }
+
+//===========================================================================
+shared_ptr<SplineSurface>
+RevEngRegion::computeLinearSwept(double tol, shared_ptr<SplineCurve>& profile,
+				 Point& pt1, Point& pt2)
+//===========================================================================
+{
+  // Axis by covariance matrix of normal vectors
+  Point axis;
+  Point Cx;
+  Point Cy;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  RevEngUtils::computeAxis(group, axis, Cx, Cy);
+
+  // Point on axis
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  double len = low.dist(high);
+  double rad;
+  Point pnt;
+  RevEngUtils::computeCylPosRadius(group, low, high,
+				   axis, Cx, Cy, pnt, rad);
+
+  // Project the points onto the defined plane 
+  vector<Point> projected;
+  double maxdp, avdp;
+  RevEngUtils::projectToPlane(group, axis, pnt, projected, maxdp, avdp);
+
+  // Approximate the projected point cloud with a spline curve
+  shared_ptr<Circle> circ(new Circle(rad, pnt, axis, Cx));
+  Point xpos;
+  vector<double> param;
+  curveApprox(projected, tol, circ, param, profile, xpos);
+
+  pt1 = pnt - len*axis;
+  pt2 = pnt + len*axis;
+  Point mid = 0.5*(pt1 + pt2);
+  
+  SweepSurfaceCreator sweep;
+  shared_ptr<SplineCurve> along(new SplineCurve(pt1, pt2));
+  shared_ptr<SplineSurface> swept_surf(sweep.linearSweptSurface(*along, *profile, mid));
+				       
+  return swept_surf;
+}
+
+//===========================================================================
+bool RevEngRegion::extractSphere(Point mainaxis[3],
+				 double tol, int min_pt, int min_pt_reg,
+				 double angtol, int prefer_elementary,
+				 vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				 vector<HedgeSurface*>& prevsfs,
+				 vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  bool found = false;
+  int min_nmb = 20;
+  //double eps = 1.0e-6;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+  
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+
+  Point adj_axis;
+  int num_pt_adj = 0;
+  for (size_t ki=0; ki<adj_elem.size(); ++ki)
+    {
+      int type = adj_elem[ki].first->instanceType();
+      if (type == Class_Cylinder || type == Class_Cone)
+	{
+	  if (adj_elem[ki].second->numPoints() > num_pt_adj)
+	    {
+	      adj_axis = adj_elem[ki].first->direction();
+	      num_pt_adj = adj_elem[ki].second->numPoints();
+	    }
+	}
+    }
+  
+  shared_ptr<Sphere> sphere = computeSphere(mainaxis, adj_axis, group_points_);
+  if (!sphere.get())
+    return false;
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofs("sph.g2");
+  sphere->writeStandardHeader(ofs);
+  sphere->write(ofs);
+#endif  
+  // Check accuracy
+  double maxd, avd; 
+  int num2, num2_2; 
+  vector<RevEngPoint*> inpt, outpt;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+  			  sphere, tol, maxd, avd, num2, num2_2, inpt, outpt,
+			  parvals, dist_ang, angtol);
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofd("in_out_sphere.g2");
+  ofd << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd << inpt.size() << std::endl;
+  for (size_t kr=0; kr<inpt.size(); ++kr)
+    ofd << inpt[kr]->getPoint() << std::endl;
+  ofd << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd << outpt.size() << std::endl;
+  for (size_t kr=0; kr<outpt.size(); ++kr)
+    ofd << outpt[kr]->getPoint() << std::endl;
+#endif
+  
+  if (inpt.size() > group_points_.size()/2 && (int)inpt.size() > min_nmb)
+    {
+      shared_ptr<Sphere> sphere_in = computeSphere(mainaxis, adj_axis, inpt);
+      vector<RevEngPoint*> inpt_in, outpt_in; //, inpt2, outpt2;
+      vector<pair<double, double> > dist_ang_in;
+      vector<double> parvals_in;
+      double maxd_in, avd_in;
+      int num2_in, num2_in2;
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      sphere_in, tol, maxd_in, avd_in, num2_in, num2_in2,
+			      inpt_in, outpt_in, parvals_in, dist_ang_in, angtol);
+  
+#ifdef DEBUG_EXTRACT
+      std::ofstream ofd2("in_out_sphere2.g2");
+      ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+      ofd2 << inpt_in.size() << std::endl;
+      for (size_t kr=0; kr<inpt_in.size(); ++kr)
+	ofd2 << inpt_in[kr]->getPoint() << std::endl;
+      ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+      ofd2 << outpt_in.size() << std::endl;
+      for (size_t kr=0; kr<outpt_in.size(); ++kr)
+	ofd2 << outpt_in[kr]->getPoint() << std::endl;
+#endif
+      
+      if (num2_in > num2 || avd_in < avd)
+	{
+	  std::swap(sphere, sphere_in);
+	  std::swap(num2, num2_in);
+	  std::swap(num2_2, num2_in2);
+	  std::swap(avd, avd_in);
+	  std::swap(maxd, maxd_in);
+	  std::swap(parvals, parvals_in);
+	  std::swap(dist_ang, dist_ang_in);
+	  // std::cout << "Sphere swap" << std::endl;
+	  // std::cout << group_points_.size() << " " << num2_in;
+	  // std::cout << " " << num2 << " " << avd_in << " " << avd;
+	  // std::cout << " " << maxd_in << " " << maxd << std::endl;
+	}
+    }
+
+  //int num = (int)group_points_.size();
+  double maxd_init, avd_init;
+  int num_init, num_init2;
+  getAccuracy(maxd_init, avd_init, num_init, num_init2);
+  int sf_flag = defineSfFlag(0, tol, num2, num2_2,
+			     avd, false);
+  if (sf_flag < ACCURACY_POOR)
+    {
+     bool OK = true;
+      if (associated_sf_.size() > 0)
+	{
+	  // Check with current approximating surface
+	  double acc_fac1 = 1.25;
+	  double acc_fac2 = 0.75;
+	  if (surfflag_ == ACCURACY_OK && sf_flag > surfflag_)
+	    OK = false;
+	  else if (surfflag_ < ACCURACY_POOR && sf_flag >= ACCURACY_POOR)
+	    OK = false;
+	  else if (prefer_elementary == ALWAYS_ELEM)
+	    OK = false;
+	  else if (prefer_elementary == PREFER_ELEM &&
+		   ((double)num2 < acc_fac1*num_init ||
+		    avd > acc_fac2*avd_init))
+	    OK = false;
+	  else if (prefer_elementary == BEST_ACCURACY &&
+		   (num2 < num_init || avd > avd_init))
+	    OK = false;
+	  if (sf_flag == ACCURACY_OK && surfflag_ > ACCURACY_OK)
+	    OK = true;
+	}
+
+      if (OK)
+	{
+	  found = true;
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	    }
+	  setAccuracy(maxd, avd, num2, num2_2);
+#ifdef DEBUG
+	  std::cout << "Sphere. N1: " << num << ", N2: " << num2 << ", max: " << maxd << ", av: " << avd << std::endl;
+#endif
+	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(sphere, this));
+	  for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+	    prevsfs.push_back(associated_sf_[kh]);
+	  setHedge(hedge.get());
+	  hedgesfs.push_back(hedge);
+	  setSurfaceFlag(sf_flag);
+	}
+    }
+  if (!basesf_.get() ||
+      (num2 >= num_in_base_ && avd < avdist_base_))
+    setBaseSf(sphere, maxd, avd, num2, num2_2);
+
+  return found;
+}
+
+//===========================================================================
+shared_ptr<Sphere> RevEngRegion::computeSphere(Point mainaxis[3],
+					       Point adj_axis,
+					       vector<RevEngPoint*>& points)
+//===========================================================================
+{
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  Point centre;
+  double radius;
+  try {
+    RevEngUtils::computeSphereProp(group, centre, radius);
+  }
+  catch (...)
+    {
+      shared_ptr<Sphere> dummy;
+      return dummy;
+    }
+
+  Point z_axis;
+  int ix = -1;
+  double minang = M_PI;
+  if (adj_axis.dimension() > 0)
+    z_axis = adj_axis;
+  else
+    {
+      Point norm = normalcone_.centre();
+      for (int ka=0; ka<3; ++ka)
+	{
+	  double ang = mainaxis[ka].angle(norm);
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang < minang)
+	    {
+	      minang = ang;
+	      ix = ka;
+	    }
+	}
+      z_axis = mainaxis[ix];
+    }
+  
+  // Define x-axis
+  ix = -1;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = mainaxis[ka].angle(z_axis);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < minang)
+	{
+	  minang = ang;
+	  ix = ka;
+	}
+    }
+
+  Point Cy = mainaxis[(ix+1)%3].cross(z_axis);
+  Point Cx = z_axis.cross(Cy);
+  
+  shared_ptr<Sphere> sph(new Sphere(radius, centre, z_axis, Cx));
+  //cyl->setParamBoundsV(-len, len);
+  return sph;
+}
+
+//===========================================================================
+bool RevEngRegion::extractCone(double tol, int min_pt, int min_pt_reg,
+			       double angtol, int prefer_elementary,
+			       vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			       vector<HedgeSurface*>& prevsfs,
+			       vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+ bool found = false;
+  int min_nmb = 20;
+  //double eps = 1.0e-6;
+  double minang = 0.05;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+  
+  Point apex;
+  shared_ptr<Cone> cone = computeCone(group_points_, apex);
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofs("conesf.g2");
+  cone->writeStandardHeader(ofs);
+  cone->write(ofs);
+#endif
+  
+  // Check accuracy
+  double maxd, avd; 
+  int num2, num2_2; 
+  vector<RevEngPoint*> inpt, outpt;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+  			  cone, tol, maxd, avd, num2, num2_2, inpt, outpt,
+			  parvals, dist_ang, angtol);
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofd("in_out_cone.g2");
+  ofd << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd << inpt.size() << std::endl;
+  for (size_t kr=0; kr<inpt.size(); ++kr)
+    ofd << inpt[kr]->getPoint() << std::endl;
+  ofd << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd << outpt.size() << std::endl;
+  for (size_t kr=0; kr<outpt.size(); ++kr)
+    ofd << outpt[kr]->getPoint() << std::endl;
+#endif
+  
+  if (inpt.size() > group_points_.size()/2 && (int)inpt.size() > min_nmb)
+    {
+      Point apex2;
+      shared_ptr<Cone> cone_in = computeCone(inpt, apex2);
+      vector<RevEngPoint*> inpt_in, outpt_in; //, inpt2, outpt2;
+      vector<pair<double, double> > dist_ang_in;
+      vector<double> parvals_in;
+      double maxd_in, avd_in;
+      int num2_in, num2_in2;
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      cone_in, tol, maxd_in, avd_in, num2_in, num2_in2, 
+			      inpt_in, outpt_in, parvals_in, dist_ang_in, angtol);
+  
+#ifdef DEBUG_EXTRACT
+      std::ofstream ofd2("in_out_cone2.g2");
+      ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+      ofd2 << inpt_in.size() << std::endl;
+      for (size_t kr=0; kr<inpt_in.size(); ++kr)
+	ofd2 << inpt_in[kr]->getPoint() << std::endl;
+      ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+      ofd2 << outpt_in.size() << std::endl;
+      for (size_t kr=0; kr<outpt_in.size(); ++kr)
+	ofd2 << outpt_in[kr]->getPoint() << std::endl;
+#endif
+      
+      if (num2_in > num2 || avd_in < avd)
+	{
+	  std::swap(cone, cone_in);
+	  std::swap(num2, num2_in);
+	  std::swap(num2_2, num2_in2);
+	  std::swap(avd, avd_in);
+	  std::swap(maxd, maxd_in);
+	  std::swap(parvals, parvals_in);
+	  std::swap(dist_ang, dist_ang_in);
+	  std::swap(apex, apex2);
+	  // std::cout << "Cone swap" << std::endl;
+	  // std::cout << group_points_.size() << " " << num2_in;
+	  // std::cout << " " << num2 << " " << avd_in << " " << avd;
+	  // std::cout << " " << maxd_in << " " << maxd << std::endl;
+	}
+    }
+
+  //int num = (int)group_points_.size();
+  double maxd_init, avd_init;
+  int num_init, num_init2;
+  getAccuracy(maxd_init, avd_init, num_init, num_init2);
+  int sf_flag = defineSfFlag(0, tol, num2, num2_2,
+			     avd, true);
+  double cone_ang = cone->getConeAngle();
+  if (0.5*M_PI-fabs(cone_ang) > minang && (!bbox_.containsPoint(apex, tol)) &&
+      sf_flag < ACCURACY_POOR)
+    {
+     bool OK = true;
+      if (associated_sf_.size() > 0)
+	{
+	  // Check with current approximating surface
+	  double acc_fac1 = 1.25;
+	  double acc_fac2 = 0.75;
+	  if (surfflag_ == ACCURACY_OK && sf_flag > surfflag_)
+	    OK = false;
+	  else if (prefer_elementary == ALWAYS_ELEM)
+	    OK = false;
+	  else if (prefer_elementary == PREFER_ELEM &&
+		   ((double)num2 < acc_fac1*num_init ||
+		    avd > acc_fac2*avd_init))
+	    OK = false;
+	  else if (prefer_elementary == BEST_ACCURACY &&
+		   (num2 < num_init || avd > avd_init))
+	    OK = false;
+	  if (sf_flag == ACCURACY_OK && surfflag_ > ACCURACY_OK)
+	    OK = true;
+	}
+
+      if (OK)
+	{
+	  found = true;
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	    }
+	  setAccuracy(maxd, avd, num2, num2_2);
+#ifdef DEBUG
+	  std::cout << "Cone. N1: " << num << ", N2: " << num2 << ", max: " << maxd << ", av: " << avd << std::endl;
+#endif
+	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(cone, this));
+	  for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+	    prevsfs.push_back(associated_sf_[kh]);
+	  setHedge(hedge.get());
+	  hedgesfs.push_back(hedge);
+	  setSurfaceFlag(sf_flag);
+	}
+    }
+  if (!basesf_.get() ||
+      (num2 >= num_in_base_ && avd < avdist_base_))
+    setBaseSf(cone, maxd, avd, num2, num2_2);
+
+  return found;
+}
+
+
+//===========================================================================
+shared_ptr<Cone> RevEngRegion::computeCone(vector<RevEngPoint*>& points, Point& apex)
+//===========================================================================
+{
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+
+  Point mid(0.0, 0.0, 0.0);
+  double wgt = 1.0/(double)points.size();
+  for (size_t kr=0; kr<points.size(); ++kr)
+    {
+      Vector3D xyz = points[kr]->getPoint();
+      Point pnt(xyz[0], xyz[1], xyz[2]);
+      mid += wgt*pnt;
+    }
+  Point axis;
+  Point Cx;
+  Point Cy;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  RevEngUtils::coneAxis(group, axis, Cx, Cy);
+
+  //Point apex;
+  double phi;
+  RevEngUtils::coneApex(group, axis, apex, phi);
+  
+  vector<Point> rotated;
+  RevEngUtils::rotateToPlane(group, Cx, axis, apex, rotated);
+#ifdef DEBUG_EXTRACT
+  double len = low.dist(high);
+  std::ofstream of("rotated_pts_cone.g2");
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << rotated.size() << std::endl;
+  for (size_t kr=0; kr<rotated.size(); ++kr)
+    of << rotated[kr] << std::endl;
+  of << "410 1 0 4 0 0 255 255" << std::endl;
+  of << "1" << std::endl;
+  of << apex-0.5*len*axis << " " << apex+0.5*len*axis << std::endl;
+  of << "410 1 0 4 0 255 0 255" << std::endl;
+  of << "1" << std::endl;
+  of << apex-0.5*len*Cx << " " << apex+0.5*len*Cx << std::endl;
+#endif
+  
+  Point pos0 = apex + ((mid - apex)*axis)*axis;
+  double dd = apex.dist(pos0);
+  double rad0 = dd*tan(phi);
+  shared_ptr<Cone> cone(new Cone(fabs(rad0), pos0, axis, Cx, phi));
+  //cone->setParamBoundsV(-0.2*len, 0.2*len);
+  
+  shared_ptr<Circle> circ(new Circle(rad0, pos0, axis, Cx));
+  vector<Point> projected;
+  double maxdp, avdp;
+  RevEngUtils::projectToPlane(group, axis, pos0, projected, maxdp, avdp);
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofp3("projected_pts_cone.g2");
+  ofp3 << "400 1 0 4 255 0 0 255" << std::endl;
+  ofp3 << projected.size() << std::endl;
+  for (size_t kr=0; kr<projected.size(); ++kr)
+    ofp3 << projected[kr] << std::endl;
+  circ->writeStandardHeader(ofp3);
+  circ->write(ofp3);
+#endif
+  
+  return cone;
+}
+
+//===========================================================================
+bool RevEngRegion::adjacentToCylinder(Point mainaxis[3],
+				      double tol, int min_pt, int min_pt_reg,
+				      double angtol, int prefer_elementary,
+				      vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				      vector<HedgeSurface*>& prevsfs,
+				      vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+
+  // Check for one and only one cylinder axis
+  Point pos, axis;
+  size_t cyl_ix;
+  for (size_t ki=0; ki<adj_elem.size(); ++ki)
+    {
+      if (adj_elem[ki].first->instanceType() == Class_Cylinder)
+	{
+	  Point loc = adj_elem[ki].first->location();
+	  Point dir = adj_elem[ki].first->direction();
+	  if (pos.dimension() == 0)
+	    {
+	      pos = loc;
+	      axis = dir;
+	      cyl_ix = ki;
+	    }
+	  else
+	    {
+	      // Check compatibility
+	      double ang = axis.angle(dir);
+	      ang = std::min(ang, M_PI-ang);
+	      if (ang > angtol)
+		return false;
+
+	      Point axis_pt = pos + ((loc - pos)*axis)*axis;
+	      double dist = loc.dist(axis_pt);
+	      if (dist > tol)
+		return false;
+	    }
+	}
+    }
+  if (pos.dimension() == 0)
+    return false;  // No adjacent cylinder
+
+  double min_ang = M_PI;
+  int ix = -1;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = mainaxis[ka].angle(axis);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < min_ang)
+	{
+	  min_ang = ang;
+	  ix = ka;
+	}
+    }
+
+  if (ix < 0)
+    return false; // Should not happen
+  
+  Point Cx = mainaxis[(ix+1)%3];
+  Point Cy = axis.cross(Cx);
+  Cy.normalize();
+  Cx = Cy.cross(axis);
+  Cx.normalize();
+  
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  RevEngUtils::rotateToPlane(group, Cy, axis, pos, rotated);
+  double len = bbox_.low().dist(bbox_.high());
+
+#ifdef DEBUG_CYL
+  std::ofstream of("rotated_pts.g2");
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << rotated.size() << std::endl;
+  for (size_t kr=0; kr<rotated.size(); ++kr)
+    of << rotated[kr] << std::endl;
+  of << "410 1 0 4 0 0 255 255" << std::endl;
+  of << "1" << std::endl;
+  of << pos-0.5*len*axis << " " << pos+0.5*len*axis << std::endl;
+  of << "410 1 0 4 0 255 0 255" << std::endl;
+  of << "1" << std::endl;
+  of << pos-0.5*len*Cy << " " << pos+0.5*len*Cy << std::endl;
+#endif
+      
+  // Approximate rotated points with a circle
+  Point centre;
+  double radius;
+  RevEngUtils::computeCircPosRadius(rotated, Cx, Cy, axis, centre, radius);
+  shared_ptr<Circle> circ(new Circle(radius, centre, Cx, Cy));
+#ifdef DEBUG_CYL
+  circ->writeStandardHeader(of);
+  circ->write(of);
+#endif
+
+  // Check accuracy
+  bool found = false;
+  double maxd, avd;
+  int num_in;
+  RevEngUtils::distToCurve(rotated, circ, tol, maxd, avd, num_in);
+  if (accuracyOK(min_pt, tol, num_in, avd))
+    {
+      // A surface can be fitted. Check if it is a torus or a sphere
+      Point axis_pt = pos + ((centre - pos)*axis)*axis;
+      double dist = centre.dist(axis_pt);
+      shared_ptr<ElementarySurface> surf;
+      if (dist < tol)
+	// Sphere
+	surf = shared_ptr<ElementarySurface>(new Sphere(radius, centre,
+							axis, Cx));
+      else
+	// Torus
+	surf = shared_ptr<ElementarySurface>(new Torus(dist, radius,
+						       axis_pt, axis, Cx));
+
+#ifdef DEBUG_CYL
+      std::ofstream of2("rot_sf.g2");
+      surf->writeStandardHeader(of2);
+      surf->write(of2);
+      adj_elem[cyl_ix].first->writeStandardHeader(of2);
+      adj_elem[cyl_ix].first->write(of2);
+#endif
+      // Check accuracy of surface
+      vector<RevEngPoint*> inpt, outpt;
+      vector<pair<double, double> > dist_ang;
+      double maxd, avd;
+      int num_in, num2_in;
+      vector<double> parvals;
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      surf, tol, maxd, avd, num_in, num2_in,
+			      inpt, outpt, parvals, dist_ang,
+			      angtol);
+#ifdef DEBUG_CYL
+      std::ofstream ofd2("in_out_elem.g2");
+      ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+      ofd2 << inpt.size() << std::endl;
+      for (size_t kr=0; kr<inpt.size(); ++kr)
+	ofd2 << inpt[kr]->getPoint() << std::endl;
+      ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+      ofd2 << outpt.size() << std::endl;
+      for (size_t kr=0; kr<outpt.size(); ++kr)
+	ofd2 << outpt[kr]->getPoint() << std::endl;
+#endif
+
+      int sf_flag = defineSfFlag(0, tol, num_in, num2_in,
+				 avd, false);
+      if (sf_flag < ACCURACY_POOR)
+	{
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	    }
+	  setAccuracy(maxd, avd, num_in, num2_in);
+	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(surf, this));
+	  setHedge(hedge.get());
+	  hedgesfs.push_back(hedge);
+	  setSurfaceFlag(sf_flag);
+	  found = true;
+	}
+      
+      if (!basesf_.get() ||
+	  (num2_in >= num_in_base_ && avd < avdist_base_))
+	setBaseSf(surf, maxd, avd, num_in, num2_in);
+    }
+  return found;
+}
+
+
+
+//===========================================================================
+bool RevEngRegion::contextCylinder(Point mainaxis[3],
+				   double tol, int min_pt, int min_pt_reg,
+				   double angtol, int prefer_elementary,
+				   vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				   vector<HedgeSurface*>& prevsfs,
+				   vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  int min_nmb = 20;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+  
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+
+  return contextCylinder(mainaxis, tol, min_pt, min_pt_reg, angtol, 
+			 prefer_elementary, adj_elem, hedgesfs, prevsfs,
+			 out_groups);
+}
+
+
+//===========================================================================
+bool RevEngRegion::contextCylinder(Point mainaxis[3],
+				   double tol, int min_pt,  int min_pt_reg,
+				   double angtol, int prefer_elementary,
+				   vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj_elem,
+				   vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				   vector<HedgeSurface*>& prevsfs,
+				   vector<vector<RevEngPoint*> >& out_groups,
+				   int mode)
+//===========================================================================
+{
+  int min_nmb = 20;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+  
+  double angtol2 = 0.1;
+  Point pos, axis, Cx;
+  double rad;
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_planar;
+  vector<RevEngPoint*> cyl_pts;
+  bool foundaxis = analyseCylinderContext(adj_elem, tol, angtol2, mainaxis,
+					  mode, adj_planar, pos, axis, Cx,
+					  rad, cyl_pts);
+  if (!foundaxis)
+    return false;
+
+  shared_ptr<Cylinder> cyl(new Cylinder(rad, pos, axis, Cx));
+
+#ifdef DEBUG_CYLCONTEXT
+  std::ofstream of("context_cyl.g2");
+  cyl->writeStandardHeader(of);
+  cyl->write(of);
+#endif
+
+  // // Fetch remaining points
+  // vector<RevEngPoint*> remaining;
+  // getRemainingPoints(cyl_pts, remaining);
+  
+  // Move points as appropriate
+  // First check accuracy with respect to cylinder
+  vector<RevEngPoint*> inpt, outpt;
+  vector<pair<double, double> > dist_ang;
+  double maxd, avd;
+  int num_in, num2_in;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(cyl_pts.begin(), cyl_pts.end(),
+			  cyl, tol, maxd, avd, num_in, num2_in,
+			  inpt, outpt, parvals, dist_ang,
+			  angtol);
+  int sf_flag_cyl = defineSfFlag((int)cyl_pts.size(), 0, tol, num_in, num2_in,
+				 avd, true);
+  if (sf_flag_cyl >= ACCURACY_POOR)
+    return false;  // Not a good subset
+
+  // Check if parts of this regions represents a likely cylinder
+  if (hasSurface())
+    {
+      double fac = 1.5;
+      if (surfflag_ < sf_flag_cyl || avdist_ < fac*avd ||
+  	  (avdist_ <= tol && avd > tol))
+  	return false;  // Not an improvement
+    }
+
+  double cyl_lim = 0.3;
+  if (num2_in < (int)(cyl_lim*(double)group_points_.size()))
+      return false;
+
+  // Extract points from cylinder
+  vector<vector<RevEngPoint*> > planar(adj_planar.size());
+  vector<RevEngPoint*> remaining;
+  vector<Point> norm(adj_planar.size());
+  vector<Point> loc(adj_planar.size());
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    {
+      norm[ki] = adj_planar[ki].first->direction();
+      Point avnorm = adj_planar[ki].second->getMeanNormal();
+      if (norm[ki]*avnorm < 0.0)
+	norm[ki] *= -1;
+      loc[ki] = adj_planar[ki].first->location();
+    }
+  
+  double cyl_dom[4];   // Cylinder domain
+  cyl_dom[0] = cyl_dom[2] = std::numeric_limits<double>::max();
+  cyl_dom[1] = cyl_dom[3] = std::numeric_limits<double>::lowest();
+  double ang_fac = 1.1;
+  double pi4 = 0.25*M_PI;
+  vector<double> ptdist(adj_planar.size());
+  vector<double> ptang(adj_planar.size());
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector3D xyz = group_points_[ki]->getPoint();
+      Point ptpos(xyz[0], xyz[1], xyz[2]);
+      Point ptnorm = group_points_[ki]->getLocFuncNormal();
+      size_t kj;
+      for (kj=0; kj<ptdist.size(); ++kj)
+	{
+	  ptdist[kj] = fabs((ptpos-loc[kj])*norm[kj]);
+	  ptang[kj] = norm[kj].angle(ptnorm);
+	  ptang[kj] = std::min(ptang[kj], M_PI-ptang[kj]);
+	}
+      double dist3 = dist_ang[ki].first;
+      Point axis_pt = pos + ((ptpos - pos)*axis)*axis;
+      double dist4 = ptpos.dist(axis_pt);
+      double ang3 = dist_ang[ki].second;
+      for (kj=0; kj<ptdist.size(); ++kj)
+	{
+	  if (ptdist[kj] < dist3 && ptang[kj] < ang_fac*ang3 &&
+	      (dist4 >= rad || axis.angle(norm[kj]) > pi4))
+	    {
+	      planar[kj].push_back(group_points_[ki]);
+	      break;
+	    }
+	}
+      if (kj == ptdist.size())
+	{
+	  remaining.push_back(group_points_[ki]);
+	  cyl_dom[0] = std::min(cyl_dom[0], parvals[2*ki]);
+	  cyl_dom[1] = std::max(cyl_dom[1], parvals[2*ki]);
+	  cyl_dom[2] = std::min(cyl_dom[2], parvals[2*ki+1]);
+	  cyl_dom[3] = std::max(cyl_dom[3], parvals[2*ki+1]);
+	}
+    }
+  
+  // Planes to cylinder
+  double eps = 1.0e-2;
+  vector<vector<RevEngPoint*> > plane2cyl(adj_planar.size());
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    {
+      for (auto it=adj_planar[ki].second->pointsBegin();
+	   it!=adj_planar[ki].second->pointsEnd(); ++it)
+	{
+	  Vector3D xyz = (*it)->getPoint();
+	  Point ptpos(xyz[0], xyz[1], xyz[2]);
+	  Point ptnorm = (*it)->getLocFuncNormal();
+	  double dist1 = (*it)->getSurfaceDist();
+	  double ang1 = norm[ki].angle(ptnorm);
+	  ang1 = std::min(ang1, M_PI-ang1);
+	  Point vec = ptpos - pos;
+	  double dist2 = (vec - (vec*axis)*vec).length();
+	  dist2 = fabs(dist2 - rad);
+	  //double dist2 = fabs((ptpos - pos)*axis);
+	  if (dist2 < dist1)
+	    {
+	      double upar, vpar, tdist;
+	      Point close;
+	      cyl->closestPoint(ptpos, upar, vpar, close, tdist, eps);
+	      Point cnorm;
+	      cyl->normal(cnorm, upar, vpar);
+	      double ang2 = cnorm.angle(ptnorm);
+	      if (ang2 <= angtol2 && vpar >= cyl_dom[2] && vpar <= cyl_dom[3])
+		plane2cyl[ki].push_back(*it);
+	    }
+	}
+    }
+  
+#ifdef DEBUG_CYLCONTEXT
+  std::ofstream of1("cyl2plane.g2");
+  for (size_t ki=0; ki<planar.size(); ++ki)
+    if (planar[ki].size() > 0)
+      {
+	of1 << "400 1 0 0" << std::endl;
+	of1 << planar[ki].size() << std::endl;
+	for (size_t kj=0; kj<planar[ki].size(); ++kj)
+	  of1 << planar[ki][kj]->getPoint() << std::endl;
+      }
+
+  std::ofstream of2("plane2cyl.g2");
+  for (size_t ki=0; ki<plane2cyl.size(); ++ki)
+    if (plane2cyl[ki].size() > 0)
+      {
+	of2 << "400 1 0 0" << std::endl;
+	of2 << plane2cyl[ki].size() << std::endl;
+	for (size_t kj=0; kj<plane2cyl[ki].size(); ++kj)
+	  of2 << plane2cyl[ki][kj]->getPoint() << std::endl;
+      }
+#endif
+  
+  // Recompute accuracy
+  vector<RevEngPoint*> inpt2, outpt2;
+  vector<pair<double, double> > dist_ang2;
+  double maxd2, avd2;
+  int num_in2, num2_in2;
+  vector<double> parvals2;
+  RevEngUtils::distToSurf(remaining.begin(), remaining.end(),
+			  cyl, tol, maxd2, avd2, num_in2, num2_in2,
+			  inpt2, outpt2, parvals2, dist_ang2,
+			  angtol);
+
+  if (num2_in2 > min_pt && num2_in2 > (int)remaining.size()/2)
+    {
+      // Check for deviant points at the boundary
+      shared_ptr<RevEngRegion> reg(new RevEngRegion(classification_type_,
+						    edge_class_type_, remaining));
+      
+      vector<RevEngPoint*> dist_points;
+      reg->identifyDistPoints(dist_ang2, tol, maxd2, avd2, dist_points);
+      if (dist_points.size() > 0)
+	{
+	  reg->extractSpesPoints(dist_points, out_groups, true);
+
+	  vector<RevEngPoint*> remaining2;
+	  remaining2 = reg->getPoints();
+	  for (size_t ki=0; ki<remaining2.size(); ++ki)
+	    remaining2[ki]->setRegion(this);
+
+	  std::swap(remaining, remaining2);
+	}
+      else
+	{
+	  for (size_t ki=0; ki<remaining.size(); ++ki)
+	    remaining[ki]->setRegion(this);
+	}
+    }
+
+  for (size_t ki=0; ki<plane2cyl.size(); ++ki)
+    if (plane2cyl[ki].size() > 0)
+      {
+	for (size_t kj=0; kj<plane2cyl[ki].size(); ++kj)
+	  plane2cyl[ki][kj]->setRegion(this);
+	remaining.insert(remaining.end(), plane2cyl[ki].begin(), plane2cyl[ki].end()); // Is now double
+      }
+  
+  // Recompute accuracy
+  Point x_axis, y_axis, z_axis;
+  cyl->getCoordinateAxes(x_axis, y_axis, z_axis);
+  Point pos2;
+  double rad2;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > points;
+  points.push_back(make_pair(remaining.begin(), remaining.end()));
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  RevEngUtils::computeCylPosRadius(points, low, high, z_axis,
+				   x_axis, y_axis, pos2, rad2);
+  shared_ptr<Cylinder> cyl2(new Cylinder(rad2, pos2, z_axis, x_axis));
+#ifdef DEBUG_CYLCONTEXT
+  std::ofstream of3("context_cyl2.g2");
+  cyl2->writeStandardHeader(of3);
+  cyl2->write(of3);
+#endif
+  
+  vector<RevEngPoint*> inpt3, outpt3;
+  vector<pair<double, double> > dist_ang3;
+  double maxd3, avd3;
+  int num_in3, num2_in3;
+  vector<double> parvals3;
+  RevEngUtils::distToSurf(remaining.begin(), remaining.end(),
+			  cyl, tol, maxd3, avd3, num_in3, num2_in3,
+			  inpt3, outpt3, parvals3, dist_ang3,
+			  angtol);
+  
+  vector<RevEngPoint*> inpt4, outpt4;
+  vector<pair<double, double> > dist_ang4;
+  double maxd4, avd4;
+  int num_in4, num2_in4;
+  vector<double> parvals4;
+  RevEngUtils::distToSurf(remaining.begin(), remaining.end(),
+			  cyl2, tol, maxd4, avd4, num_in4, num2_in4,
+			  inpt4, outpt4, parvals4, dist_ang4,
+			  angtol);
+  if (avd4 < avd3 && num_in4+num2_in4 < num_in3+num2_in3)
+    {
+      std::swap(maxd4, maxd3);
+      std::swap(avd4, avd3);
+      std::swap(num_in4, num_in3);
+      std::swap(num2_in4, num2_in3);
+      std::swap(parvals4, parvals3);
+      std::swap(dist_ang4, dist_ang3);
+      std::swap(inpt4, inpt3);
+      std::swap(outpt4, outpt3);
+    }
+  
+  // Move points
+#ifdef DEBUG_CYLCONTEXT
+  std::ofstream of4("cyl_pts.g2");
+  writeRegionPoints(of4);
+#endif
+  
+  int sf_flag = defineSfFlag((int)remaining.size(), 0, tol, num_in3, num2_in3, avd3, true);
+  bool OKsurf = (sf_flag < ACCURACY_POOR);
+  bool hasSurf = (associated_sf_.size() > 0);
+  if (hasSurf)
+    {
+      double acc_fac = 1.5;
+      double maxd_init, avd_init;
+      int num_init, num_init2;
+      getAccuracy(maxd_init, avd_init, num_init, num_init2);
+      int sfcode;
+      int sftype = associated_sf_[0]->instanceType(sfcode);
+      double ang = (sftype == Class_Plane) ?
+	normalcone_.angle() : M_PI;
+      double ang_lim = 0.1*M_PI;
+	      
+      // Check with current approximating surface
+      if (surfflag_ == ACCURACY_OK && sf_flag > surfflag_)
+	OKsurf = false;
+      else if (num_init > num_in3 && num_init2 > num2_in3 && avd_init < avd3)
+	OKsurf = false;
+      else if (prefer_elementary == ALWAYS_ELEM ||
+	       prefer_elementary == PREFER_ELEM)
+	{
+	  if (!(ang > ang_lim && (num_in3 < num_init ||
+				  (avd3 < avd_init &&
+				   num_in3 < acc_fac*num_init))))
+	    OKsurf = false;
+	}
+      else
+	{
+	  if (!(num_in3 < num_init ||
+		(avd3 < avd_init && num_in3 < acc_fac*num_init)))
+	    OKsurf = true;
+	}
+      if (sf_flag == ACCURACY_OK && surfflag_ > ACCURACY_OK)
+	OKsurf = true;
+    }
+
+
+  if (OKsurf || hasSurf == false)
+    {
+      std::swap(group_points_, remaining);
+      for (size_t ki=0; ki<plane2cyl.size(); ++ki)
+	if (plane2cyl[ki].size() > 0)
+	  {
+	    for (size_t kj=0; kj<plane2cyl[ki].size(); ++kj)
+	      {
+		adj_planar[ki].second->removePoint(plane2cyl[ki][kj]);
+		plane2cyl[ki][kj]->setRegion(this);
+	      }
+	    adj_planar[ki].second->updateInfo();
+	  }
+    }
+
+
+  if (OKsurf)
+    {
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  group_points_[ki]->setPar(Vector2D(parvals3[2*ki],parvals3[2*ki+1]));
+	  group_points_[ki]->setSurfaceDist(dist_ang3[ki].first, dist_ang3[ki].second);
+	}
+      setAccuracy(maxd3, avd3, num_in3, num2_in3);
+      shared_ptr<HedgeSurface> hedge(new HedgeSurface(cyl, this));
+      setHedge(hedge.get());
+      hedgesfs.push_back(hedge);
+      setSurfaceFlag(sf_flag);
+    }
+  
+  if (OKsurf || hasSurf == false)
+    {
+      for (size_t kj=0; kj<planar.size(); ++kj)
+	{
+	  if (planar[kj].size() > 0)
+	    {
+	      // Parameterize
+	      vector<RevEngPoint*> inpt4, outpt4;
+	      vector<pair<double, double> > dist_ang4;
+	      double maxd4, avd4;
+	      int num_in4, num2_in4;
+	      vector<double> parvals4;
+	      RevEngUtils::distToSurf(planar[kj].begin(), planar[kj].end(),
+				      adj_planar[kj].first, tol, maxd4, avd4,
+				      num_in4, num2_in4, inpt4, outpt4, parvals4,
+				      dist_ang4, angtol);
+	      for (size_t ki=0; ki<planar[kj].size(); ++ki)
+		{
+		  planar[kj][ki]->setPar(Vector2D(parvals4[2*ki],parvals4[2*ki+1]));
+		  planar[kj][ki]->setSurfaceDist(dist_ang4[ki].first, dist_ang4[ki].second);
+		  planar[kj][ki]->setRegion(adj_planar[kj].second);
+		  adj_planar[kj].second->addPoint(planar[kj][ki]);
+		}
+	      adj_planar[kj].second->updateInfo();
+	  
+	      double maxd5, avd5;
+	      int num_5, num2_5;
+	      adj_planar[kj].second->getAccuracy(maxd5, avd5, num_5, num2_5);
+	      int sf_flag2 =
+		adj_planar[kj].second->defineSfFlag(0, tol, num_5,
+						    num2_5, avd5, false);
+	  
+	      adj_planar[kj].second->setSurfaceFlag(sf_flag2); 
+	    }
+	}
+  
+
+      for (size_t ki=0; ki<adj_planar.size(); ++ki)
+	{
+	  int num = adj_planar[ki].second->numPoints();
+	  for (int ka=0; ka<num; ++ka)
+	    {
+	      RevEngRegion *reg = adj_planar[ki].second->getPoint(ka)->region();
+	      if (reg != adj_planar[ki].second)
+		std::cout << "contextCylinder. Region mismatch " << reg << " " << adj_planar[ki].second << std::endl;
+	    }
+	}
+      
+      // Ensure that the point group is connected
+      vector<vector<RevEngPoint*> > added_groups;
+      splitRegion(added_groups);
+      if (added_groups.size() > 0)
+	out_groups.insert(out_groups.end(), added_groups.begin(), added_groups.end());
+      
+      updateInfo(tol, angtol);
+    }
+  else
+    {
+      for (size_t ki=0; ki<plane2cyl.size(); ++ki)
+	if (plane2cyl[ki].size() > 0)
+	  {
+	    for (size_t kj=0; kj<plane2cyl[ki].size(); ++kj)
+	      {
+		plane2cyl[ki][kj]->setRegion(adj_planar[ki].second);
+	      }
+	  }
+      for (size_t ki=0; ki<out_groups.size(); ++ki)
+	for (size_t kj=0; kj<out_groups[ki].size(); ++kj)
+	  out_groups[ki][kj]->setRegion(this);
+      out_groups.clear();
+    }
+    
+  return OKsurf;
+  //return false;
+}
+
+//===========================================================================
+bool RevEngRegion::contextTorus(Point mainaxis[3],
+				double tol, int min_pt,  int min_pt_reg,
+				double angtol, int prefer_elementary,
+				vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				vector<HedgeSurface*>& prevsfs,
+				vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  //bool found = false;
+  int min_nmb = 20;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+  
+
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+
+  Point pos, axis, Cx;
+  vector<size_t> adj_ix;
+  double R1, R2, R1_tor;
+  double cyl_dom[4];
+  bool outer;
+  int plane_ix, cyl_ix;
+  bool analyse_rotated = false;
+  bool foundaxis = analyseTorusContext(adj_elem, tol, angtol, adj_ix,
+				       plane_ix, cyl_ix, pos, axis, Cx,
+				       R1, R2, cyl_dom, outer, analyse_rotated);
+  if (!foundaxis)
+    return false;
+
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  double len = low.dist(high);
+  if (analyse_rotated)
+    {
+      vector<Point> rotated0;
+      vector<pair<vector<RevEngPoint*>::iterator,
+		  vector<RevEngPoint*>::iterator> > group0;
+      group0.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+      RevEngUtils::rotateToPlane(group0, Cx, axis, pos, rotated0);
+#ifdef DEBUG_TORUSCONTEXT
+      std::ofstream of3("rotated_pts_contexttor.g2");
+      of3 << "400 1 0 4 255 0 0 255" << std::endl;
+      of3 << rotated0.size() << std::endl;
+      for (size_t kr=0; kr<rotated0.size(); ++kr)
+	of3 << rotated0[kr] << std::endl;
+      of3 << "410 1 0 4 0 0 255 255" << std::endl;
+      of3 << "1" << std::endl;
+      of3 << pos-0.5*len*axis << " " << pos+0.5*len*axis << std::endl;
+      of3 << "410 1 0 4 0 255 0 255" << std::endl;
+      of3 << "1" << std::endl;
+      of3 << pos-0.5*len*Cx << " " << pos+0.5*len*Cx << std::endl;
+#endif
+    }
+  
+  double eps = 1.0e-6;
+  Point pos2 = pos - R2*axis;
+  Point Cy = axis.cross(Cx);
+  shared_ptr<Torus> tor0(new Torus(R1, R2, pos2, axis, Cx));
+  R1_tor = R1;
+  
+#ifdef DEBUG_TORUSCONTEXT
+  std::ofstream oft("torus_context.g2");
+  tor0->writeStandardHeader(oft);
+  tor0->write(oft);
+#endif
+
+  shared_ptr<ElementarySurface> cyl = adj_elem[cyl_ix].first;
+  shared_ptr<ElementarySurface> plane = adj_elem[plane_ix].first;
+  double rad = cyl->radius(0.0, 0.0);
+
+  // Extract the points feasible for a torus adjacent to the
+  // identified cylinder, and check if they constitute a point cloud
+  // of sufficiant size
+  double limit = 1.1*R2;
+  vector<RevEngPoint*> inside, outside;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      Vector3D xyz = group_points_[kr]->getPoint();
+      Point xyz2(xyz[0], xyz[1], xyz[2]);
+      Point onaxis = pos + ((xyz2-pos)*axis)*axis;
+      double dd = xyz2.dist(onaxis);
+      if (dd >= rad-limit && dd <= rad+limit)
+	inside.push_back(group_points_[kr]);
+      else
+	outside.push_back(group_points_[kr]);
+    }
+  
+  if ((int)inside.size() < min_pt_reg)
+    return false;
+  
+  //#ifdef DEBUG_TORUSCONTEXT
+  // Check accuracy of inside points with respect to the current
+  // torus
+  vector<RevEngPoint*> inpt0, outpt0;
+  vector<pair<double, double> > dist_ang0;
+  double maxd0, avd0;
+  int num_in0, num2_in0;
+  vector<double> parvals0;
+  RevEngUtils::distToSurf(inside.begin(), inside.end(),
+			  tor0, tol, maxd0, avd0, num_in0, num2_in0,
+			  inpt0, outpt0, parvals0, dist_ang0,
+			  angtol);
+  
+  //#endif
+
+  // Identify points belonging to the adjacent cylinder
+  // First rotate current point cloud according to the found axis
+  vector<RevEngPoint*> cyl_pts, remaining;
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(inside.begin(), inside.end()));
+  RevEngUtils::rotateToPlane(group, Cx, axis, pos, rotated);
+  double angtol2 = angtol;
+  if (adj_elem[cyl_ix].second->getSurfaceFlag() == PROBABLE_HELIX)
+    angtol2 = 0.5*M_PI;  // Cannot expect angular accuracy for a helix
+  RevEngUtils::extractLinearPoints(inside, rotated,
+				   len, pos, axis, rad, axis, false,
+				   tol, angtol2, dist_ang0,
+				   cyl_pts, true, remaining);
+  
+#ifdef DEBUG_TORUSCONTEXT
+  std::ofstream of3("rotated_pts_contexttor.g2");
+  of3 << "400 1 0 4 255 0 0 255" << std::endl;
+  of3 << rotated.size() << std::endl;
+  for (size_t kr=0; kr<rotated.size(); ++kr)
+    of3 << rotated[kr] << std::endl;
+  of3 << "410 1 0 4 0 0 255 255" << std::endl;
+  of3 << "1" << std::endl;
+  of3 << pos-0.5*len*axis << " " << pos+0.5*len*axis << std::endl;
+  of3 << "410 1 0 4 0 255 0 255" << std::endl;
+  of3 << "1" << std::endl;
+  of3 << pos-0.5*len*Cx << " " << pos+0.5*len*Cx << std::endl;
+
+  std::ofstream ofl("lin_extract1.g2");
+  if (cyl_pts.size() > 0)
+    {
+      ofl << "400 1 0 4 55 100 100 255" << std::endl;
+      ofl << cyl_pts.size() << std::endl;
+      for (size_t kr=0; kr<cyl_pts.size(); ++kr)
+	ofl << cyl_pts[kr]->getPoint() << std::endl;
+    }
+  
+  if (remaining.size() > 0)
+    {
+      ofl << "400 1 0 4 100 100 55 255" << std::endl;
+      ofl << remaining.size() << std::endl;
+      for (size_t kr=0; kr<remaining.size(); ++kr)
+	ofl << remaining[kr]->getPoint() << std::endl;
+    }
+  
+  if (outside.size() > 0)
+    {
+      ofl << "400 1 0 4 10 10 10 255" << std::endl;
+      ofl << outside.size() << std::endl;
+      for (size_t kr=0; kr<outside.size(); ++kr)
+	ofl << outside[kr]->getPoint() << std::endl;
+    }
+#endif
+
+   // Recompute torus
+  double cpdist = R2;
+  double up, vp, dd;
+  Point cl;
+  for (size_t kr=0; kr<cyl_pts.size(); ++kr)
+    {
+      Vector3D xyz = cyl_pts[kr]->getPoint();
+      Point xyz2(xyz[0], xyz[1], xyz[2]);
+      adj_elem[plane_ix].first->closestPoint(xyz2, up, vp, cl, dd, eps);
+      cpdist = std::min(cpdist, dd);
+    }
+  Point pos3 = pos - cpdist*axis;
+  int sgn = (outer) ? -1 : 1;
+  R1_tor = rad+sgn*cpdist;
+  shared_ptr<Torus> tor(new Torus(R1_tor, cpdist, pos3, axis, Cx));
+
+#ifdef DEBUG_TORUSCONTEXT
+  tor->writeStandardHeader(oft);
+  tor->write(oft);
+#endif
+
+  // Check accuracy of identified cylinder points with respect to the
+  // adjacent cylinder
+  vector<pair<double, double> > dist_ang_cyl;
+  double maxd_cyl, avd_cyl;
+  int num_in_cyl, num2_in_cyl;
+  vector<double> parvals_cyl;
+  if (cyl_pts.size() > 0)
+    {
+      vector<RevEngPoint*> inpt_cyl, outpt_cyl;
+      RevEngUtils::distToSurf(cyl_pts.begin(), cyl_pts.end(),
+			      cyl, tol, maxd_cyl, avd_cyl, num_in_cyl, num2_in_cyl,
+			      inpt_cyl, outpt_cyl, parvals_cyl, dist_ang_cyl,
+			      angtol);
+      int sf_flag_cyl = defineSfFlag((int)cyl_pts.size(), 0, tol, num_in_cyl,
+				     num2_in_cyl, avd_cyl, true);
+      if (sf_flag_cyl >= ACCURACY_POOR)
+	{
+	  // Something unexpected happened. Return to previous stage
+	  R1_tor = R1;
+	  tor = tor0;
+	  remaining = inside;
+	  cyl_pts.clear();
+	}
+    }
+
+  // Compute torus accuracy
+  vector<RevEngPoint*> inpt, outpt;
+  vector<pair<double, double> > dist_ang;
+  double maxd, avd;
+  int num_in, num2_in;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(remaining.begin(), remaining.end(),
+			  tor, tol, maxd, avd, num_in, num2_in,
+			  inpt, outpt, parvals, dist_ang,
+			  angtol);
+
+  if (hasSurface())
+    {
+      double fac = 1.5;
+      int sf_flag_tor = defineSfFlag((int)remaining.size(), 0, tol,
+				     num_in, num2_in, avd, false);
+      if (surfflag_ < sf_flag_tor || avdist_ < fac*avd ||
+	  (avdist_ <= tol && avd > tol))
+	return false;  // Not an improvement
+    }
+  
+  // Check if parts of this regions represents a likely torus
+  double tor_lim = 0.3;
+  if (num2_in < (int)(tor_lim*(double)remaining.size()))
+    {
+      // Check if a candidate segmentation strategy is found
+      if (outside.size() > group_points_.size()/10 &&
+	  outside.size() > min_pt_reg)
+	{
+	  shared_ptr<SegmentData> seg_info(new SegmentData(1, pos, axis,
+							   rad-limit,
+							   rad+limit));
+	  seg_info_.push_back(seg_info);
+	}
+      
+      return false;
+    }
+  
+  // Extract planar points and additional cylindrical points from torus
+  vector<RevEngPoint*> planar;
+  vector<RevEngPoint*> remaining2;
+  Point axis2 = cyl->direction();
+  double pihalf = 0.5*M_PI;
+  double tor_dom[4];   // Torus domain
+  tor_dom[0] = tor_dom[2] = std::numeric_limits<double>::max();
+  tor_dom[1] = tor_dom[3] = std::numeric_limits<double>::lowest();
+  for (size_t ki=0; ki<remaining.size(); ++ki)
+    {
+      Vector3D xyz = remaining[ki]->getPoint();
+      Point ptpos(xyz[0], xyz[1], xyz[2]);
+      Point norm = remaining[ki]->getLocFuncNormal();
+      Point norm2 = remaining[ki]->getTriangNormal();
+      double dist = pos.dist(ptpos);
+      double ang1 = axis.angle(norm);
+      double ang2 = axis.angle(norm2);
+      double ang3 = axis2.angle(norm);
+      double ang4 = axis2.angle(norm2);
+      if (((dist < R1_tor && outer) || (dist > R1_tor && (!outer))) &&
+	  (ang1 <= angtol2 || ang2 <= angtol2))
+	planar.push_back(remaining[ki]);
+      else if (fabs(pihalf-ang3) <= dist_ang[ki].second ||
+	       fabs(pihalf-ang4) <= dist_ang[ki].second ||
+	       dist_ang[ki].first > tol)
+	{
+	  double upar, vpar, tdist;
+	  Point close;
+	  cyl->closestPoint(ptpos, upar, vpar, close, tdist, eps);
+	  bool OKcyl = false;
+	  if (tdist <= dist_ang[ki].first && upar >= cyl_dom[0] &&
+	      upar <= cyl_dom[1] && vpar >= cyl_dom[2] && vpar <= cyl_dom[3])
+	    cyl_pts.push_back(remaining[ki]);
+	  else
+	    {
+	      remaining2.push_back(remaining[ki]);
+	      tor_dom[0] = std::min(tor_dom[0], parvals[2*ki]);
+	      tor_dom[1] = std::max(tor_dom[1], parvals[2*ki]);
+	      tor_dom[2] = std::min(tor_dom[2], parvals[2*ki+1]);
+	      tor_dom[3] = std::max(tor_dom[3], parvals[2*ki+1]);
+	    }
+	}
+      else
+	{
+	  remaining2.push_back(remaining[ki]);
+	  tor_dom[0] = std::min(tor_dom[0], parvals[2*ki]);
+	  tor_dom[1] = std::max(tor_dom[1], parvals[2*ki]);
+	  tor_dom[2] = std::min(tor_dom[2], parvals[2*ki+1]);
+	  tor_dom[3] = std::max(tor_dom[3], parvals[2*ki+1]);
+	}
+    }
+  
+  // Plane to torus
+  vector<RevEngPoint*>  plan2tor;
+  for (auto it=adj_elem[plane_ix].second->pointsBegin();
+       it!=adj_elem[plane_ix].second->pointsEnd(); ++it)
+    {
+      Vector3D xyz = (*it)->getPoint();
+      Point ptpos(xyz[0], xyz[1], xyz[2]);
+      Point norm = (*it)->getLocFuncNormal();
+      double dist = pos.dist(ptpos);
+      double ang = axis.angle(norm);
+      ang = std::min(ang, M_PI-ang);
+      if ((dist > R1 && outer) || (dist < R1 && (!outer)))
+	{
+	  double upar, vpar, tdist;
+	  Point close;
+	  tor->closestPoint(ptpos, upar, vpar, close, tdist, eps);
+	  Point tnorm;
+	  tor->normal(tnorm, upar, vpar);
+	  double ang2 = tnorm.angle(norm);
+	  if (tdist < (*it)->getSurfaceDist() && ang2 <= angtol2 &&
+	      upar >= tor_dom[0] && upar <= tor_dom[1])
+	    plan2tor.push_back(*it);
+	}
+    }
+
+   // Cylinder to torus
+  vector<RevEngPoint*>  cyl2tor;
+  for (auto it=adj_elem[cyl_ix].second->pointsBegin();
+       it!=adj_elem[cyl_ix].second->pointsEnd(); ++it)
+    {
+      Vector3D xyz = (*it)->getPoint();
+      Vector2D uv = (*it)->getPar();
+      Point ptpos(xyz[0], xyz[1], xyz[2]);
+      Point norm = (*it)->getLocFuncNormal();
+      if (uv[0] < cyl_dom[0] || uv[0] > cyl_dom[1] ||
+	  uv[1] < cyl_dom[2] || uv[1] > cyl_dom[3])
+	{
+	  double upar, vpar, tdist;
+	  Point close;
+	  tor->closestPoint(ptpos, upar, vpar, close, tdist, eps);
+	  Point tnorm;
+	  tor->normal(tnorm, upar, vpar);
+	  double ang2 = tnorm.angle(norm);
+	  if (tdist < (*it)->getSurfaceDist() && ang2 <= angtol2)
+	    cyl2tor.push_back(*it);
+	}
+    }
+  
+#ifdef DEBUG_TORUSCONTEXT
+  if (planar.size() > 0)
+    {
+      std::ofstream ofp("planar_move.g2");
+      ofp << "400 1 0 4 255 0 0 255" << std::endl;
+      ofp << planar.size() << std::endl;
+      for (size_t kr=0; kr<planar.size(); ++kr)
+	ofp << planar[kr]->getPoint() << std::endl;
+    }
+
+  if (plan2tor.size() > 0)
+    {
+      std::ofstream oftp("plan_tor_move.g2");
+      oftp << "400 1 0 4 0 255 0 255" << std::endl;
+      oftp << plan2tor.size() << std::endl;
+      for (size_t kr=0; kr<plan2tor.size(); ++kr)
+	oftp << plan2tor[kr]->getPoint() << std::endl;
+    }
+  
+  if (cyl_pts.size() > 0)
+    {
+      std::ofstream ofc("cyl_move.g2");
+      ofc << "400 1 0 4 255 0 0 255" << std::endl;
+      ofc << cyl_pts.size() << std::endl;
+      for (size_t kr=0; kr<cyl_pts.size(); ++kr)
+	ofc << cyl_pts[kr]->getPoint() << std::endl;
+    }
+
+  if (cyl2tor.size() > 0)
+    {
+      std::ofstream oftc("cyl_tor_move.g2");
+      oftc << "400 1 0 4 0 255 0 255" << std::endl;
+      oftc << cyl2tor.size() << std::endl;
+      for (size_t kr=0; kr<cyl2tor.size(); ++kr)
+	oftc << cyl2tor[kr]->getPoint() << std::endl;
+    }
+#endif
+
+  // Recompute accuracy
+  vector<RevEngPoint*> inpt2, outpt2;
+  vector<pair<double, double> > dist_ang2;
+  double maxd2, avd2;
+  int num_in2, num2_in2;
+  vector<double> parvals2;
+  RevEngUtils::distToSurf(remaining2.begin(), remaining2.end(),
+			  tor, tol, maxd2, avd2, num_in2, num2_in2,
+			  inpt2, outpt2, parvals2, dist_ang2,
+			  angtol);
+#ifdef DEBUG_TORUSCONTEXT
+  std::ofstream ofd2("in_out_tor_context.g2");
+  ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd2 << inpt2.size() << std::endl;
+  for (size_t kr=0; kr<inpt2.size(); ++kr)
+    ofd2 << inpt2[kr]->getPoint() << std::endl;
+  ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd2 << outpt2.size() << std::endl;
+  for (size_t kr=0; kr<outpt2.size(); ++kr)
+    ofd2 << outpt2[kr]->getPoint() << std::endl;
+#endif
+      
+  if (num2_in2 > min_pt && num2_in2 > (int)remaining2.size()/2)
+    {
+      // Check for deviant points at the boundary
+      shared_ptr<RevEngRegion> reg(new RevEngRegion(classification_type_,
+						    edge_class_type_, remaining2));
+      
+      vector<RevEngPoint*> dist_points;
+      reg->identifyDistPoints(dist_ang2, tol, maxd2, avd2, dist_points);
+      if (dist_points.size() > 0)
+	{
+	  reg->extractSpesPoints(dist_points, out_groups, true);
+
+	  vector<RevEngPoint*> remaining3;
+	  remaining3 = reg->getPoints();
+	  for (size_t ki=0; ki<remaining3.size(); ++ki)
+	    remaining3[ki]->setRegion(this);
+
+	  std::swap(remaining2, remaining3);
+	}
+      else
+	{
+	  for (size_t ki=0; ki<remaining2.size(); ++ki)
+	    remaining2[ki]->setRegion(this);
+	}
+    }
+
+  if (plan2tor.size() > 0)
+    {
+      remaining2.insert(remaining2.end(), plan2tor.begin(), plan2tor.end());
+    }
+  
+  if (cyl2tor.size() > 0)
+     {
+      remaining2.insert(remaining2.end(), cyl2tor.begin(), cyl2tor.end());
+    }
+   
+  // Recompute accuracy
+  vector<RevEngPoint*> inpt3, outpt3;
+  vector<pair<double, double> > dist_ang3;
+  double maxd3, avd3;
+  int num_in3, num2_in3;
+  vector<double> parvals3;
+  RevEngUtils::distToSurf(remaining2.begin(), remaining2.end(),
+			  tor, tol, maxd3, avd3, num_in3, num2_in3,
+			  inpt3, outpt3, parvals3, dist_ang3,
+			  angtol);
+#ifdef DEBUG_TORUSCONTEXT
+  std::ofstream ofd3("in_out_tor_context3.g2");
+  ofd3 << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd3 << inpt3.size() << std::endl;
+  for (size_t kr=0; kr<inpt3.size(); ++kr)
+    ofd3 << inpt3[kr]->getPoint() << std::endl;
+  ofd3 << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd3 << outpt3.size() << std::endl;
+  for (size_t kr=0; kr<outpt3.size(); ++kr)
+    ofd3 << outpt3[kr]->getPoint() << std::endl;
+#endif
+      
+  // Check if this surface is better than an eventual previous surface. Move points
+  int sf_flag = defineSfFlag((int)remaining2.size(), 0, tol, num_in3, num2_in3, avd3, false);
+  bool OKsurf = (sf_flag < ACCURACY_POOR);
+  bool hasSurf = (associated_sf_.size() > 0);
+  if (hasSurf)
+    {
+      double acc_fac = 1.5;
+      double maxd_init, avd_init;
+      int num_init, num_init2;
+      getAccuracy(maxd_init, avd_init, num_init, num_init2);
+	      
+      // Check with current approximating surface
+      if (surfflag_ == ACCURACY_OK && sf_flag > surfflag_)
+	OKsurf = false;
+     else if (prefer_elementary == ALWAYS_ELEM ||
+	       prefer_elementary == PREFER_ELEM)
+	{
+	  if ((double)num_init/(double)group_points_.size() >
+	      (double)num_in3/(double)remaining.size() &&
+	      (avd_init <= avd3 || num_in3 >= acc_fac*num_init))
+	    OKsurf = false;
+	}
+     else
+       {
+	 if (!(num_in3 < num_init ||
+	       (avd3 < avd_init && num_in3 < acc_fac*num_init)))
+	   OKsurf = true;
+       }
+      if (sf_flag == ACCURACY_OK && surfflag_ > ACCURACY_OK)
+	OKsurf = true;
+    }
+
+  if (OKsurf || hasSurf == false)
+    {
+      std::swap(group_points_, remaining2);
+      if (plan2tor.size() > 0)
+	{
+	  for (size_t kj=0; kj<plan2tor.size(); ++kj)
+	    {
+	      adj_elem[plane_ix].second->removePoint(plan2tor[kj]);
+	      plan2tor[kj]->setRegion(this);
+	    }
+	  adj_elem[plane_ix].second->updateInfo(tol, angtol);
+	}
+      
+      if (cyl2tor.size() > 0)
+	{
+	  for (size_t kj=0; kj<cyl2tor.size(); ++kj)
+	    {
+	      adj_elem[cyl_ix].second->removePoint(cyl2tor[kj]);
+	      cyl2tor[kj]->setRegion(this);
+	    }
+	  adj_elem[cyl_ix].second->updateInfo(tol, angtol);
+	}
+    }
+  
+  if (OKsurf)
+    {
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  group_points_[ki]->setPar(Vector2D(parvals3[2*ki],parvals3[2*ki+1]));
+	  group_points_[ki]->setSurfaceDist(dist_ang3[ki].first, dist_ang3[ki].second);
+	}
+      setAccuracy(maxd3, avd3, num_in3, num2_in3);
+      shared_ptr<HedgeSurface> hedge(new HedgeSurface(tor, this));
+      setHedge(hedge.get());
+      hedgesfs.push_back(hedge);
+      setSurfaceFlag(sf_flag);
+    }
+  if (OKsurf || hasSurf == false)
+    {
+      if (planar.size() > 0)
+	{
+	  // Parameterize
+	  vector<RevEngPoint*> inpt4, outpt4;
+	  vector<pair<double, double> > dist_ang4;
+	  double maxd4, avd4;
+	  int num_in4, num2_in4;
+	  vector<double> parvals4;
+	  RevEngUtils::distToSurf(planar.begin(), planar.end(),
+				  plane, tol, maxd4, avd4, num_in4, num2_in4,
+				  inpt4, outpt4, parvals4, dist_ang4,
+				  angtol);
+	  for (size_t ki=0; ki<planar.size(); ++ki)
+	    {
+	      planar[ki]->setPar(Vector2D(parvals4[2*ki],parvals4[2*ki+1]));
+	      planar[ki]->setSurfaceDist(dist_ang4[ki].first, dist_ang4[ki].second);
+	      adj_elem[plane_ix].second->addPoint(planar[ki]);
+	    }
+	  adj_elem[plane_ix].second->updateInfo(tol, angtol);
+
+	  double maxd5, avd5;
+	  int num_in5, num2_in5;
+	  adj_elem[plane_ix].second->getAccuracy(maxd5, avd5, num_in5, num2_in5);
+	  int sf_flag5 = adj_elem[plane_ix].second->defineSfFlag(0, tol,
+								 num_in5, num2_in5,
+								 avd5, false);
+	  adj_elem[plane_ix].second->setSurfaceFlag(sf_flag5);
+	}
+  
+      if (cyl_pts.size() > 0)
+	{
+	  // Parameterize
+	  vector<RevEngPoint*> inpt4, outpt4;
+	  vector<pair<double, double> > dist_ang4;
+	  double maxd4, avd4;
+	  int num_in4, num2_in4;
+	  vector<double> parvals4;
+	  RevEngUtils::distToSurf(cyl_pts.begin(), cyl_pts.end(),
+				  cyl, tol, maxd4, avd4, num_in4, num2_in4,
+				  inpt4, outpt4, parvals4, dist_ang4,
+				  angtol);
+	  for (size_t ki=0; ki<cyl_pts.size(); ++ki)
+	    {
+	      cyl_pts[ki]->setPar(Vector2D(parvals4[2*ki],parvals4[2*ki+1]));
+	      cyl_pts[ki]->setSurfaceDist(dist_ang4[ki].first, dist_ang4[ki].second);
+	      adj_elem[cyl_ix].second->addPoint(cyl_pts[ki]);
+	    }
+	  adj_elem[cyl_ix].second->updateInfo(tol, angtol);
+
+	  double maxd5, avd5;
+	  int num_in5, num2_in5;
+	  adj_elem[cyl_ix].second->getAccuracy(maxd5, avd5, num_in5, num2_in5);
+	  int sf_flag5 = adj_elem[cyl_ix].second->defineSfFlag(0, tol,
+							       num_in5, num2_in5,
+							       avd5, true);
+	  adj_elem[cyl_ix].second->setSurfaceFlag(sf_flag5);
+	}
+
+      if (numPoints() > 0)//group_points_.size() > 0)
+	{
+	  // Ensure that the point group is connected
+	  vector<vector<RevEngPoint*> > added_groups;
+	  splitRegion(added_groups);
+	  if (added_groups.size() > 0)
+	    out_groups.insert(out_groups.end(), added_groups.begin(), added_groups.end());
+	  
+	  updateInfo(tol, angtol);
+	}
+      else
+	{
+	  removeFromAdjacent();
+	  clearRegionAdjacency();
+	}
+    }
+
+
+  if (outside.size() > 0)
+    {
+      vector<std::vector<RevEngPoint*> > sep_outside;
+      std::vector<RevEngPoint*> dummy;
+      connectedGroups(outside, sep_outside, false, dummy);
+      out_groups.insert(out_groups.end(), sep_outside.begin(),
+			sep_outside.end());
+    }
+  
+  return OKsurf;
+ 
+ }
+
+//===========================================================================
+bool RevEngRegion::addPointsToGroup(vector<RevEngPoint*>& points,
+				    double tol, double angtol, bool compute_accuracy)
+//===========================================================================
+{
+  if (!hasSurface())
+    return false;
+
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+
+  if (compute_accuracy)
+    {
+      // Compute accuracy
+      vector<RevEngPoint*> inpt, outpt;
+      vector<pair<double, double> > dist_ang;
+      double maxd, avd;
+      int num_in, num2_in;
+      vector<double> parvals;
+      RevEngUtils::distToSurf(points.begin(), points.end(),
+			      surf, tol, maxd, avd, num_in, num2_in,
+			      inpt, outpt, parvals, dist_ang,
+			      angtol);
+
+      // Update info in points
+      for (size_t kh=0; kh<points.size(); ++kh)
+	{
+	  points[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	  points[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	}
+    }
+  for (size_t kh=0; kh<points.size(); ++kh)
+      points[kh]->setRegion(this);
+  group_points_.insert(group_points_.end(), points.begin(), points.end());
+  updateInfo(tol, angtol);
+
+  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		  surf->instanceType() == Class_Cone);
+  int sf_flag = defineSfFlag(0, tol, num_inside_, num_inside2_, avdist_, cyllike);
+  setSurfaceFlag(sf_flag);
+
+  return true;
+}
+
+
+
+//===========================================================================
+void RevEngRegion::growInDomain(RevEngRegion *adjacent, double tol,
+				double angtol)
+//===========================================================================
+{
+  if (!hasSurface())
+    return;   // No growing is possible
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  shared_ptr<ParamSurface> surf2;
+  if (adjacent->hasSurface())
+    surf2 = adjacent->getSurface(0)->surface();
+
+  // Get seed points
+  vector<RevEngPoint*> pts = adjacent->extractNextToAdjacent(this);
+
+  vector<RevEngPoint*> move;
+  double eps = 1.0e-6;
+  for (size_t ki=0; ki<pts.size(); ++ki)
+    {
+      pts[ki]->setVisited();
+      Vector3D xyz = pts[ki]->getPoint();
+      Point pos(xyz[0], xyz[1], xyz[2]);
+      double upar, vpar, dist;
+      Point close;
+      surf->closestPoint(pos, upar, vpar, close, dist, eps);
+      bool in_domain = surf->inDomain(upar, vpar, eps);
+      bool at_boundary = surf->onBoundary(upar, vpar, eps);
+      if (in_domain && (!at_boundary))
+	{
+	  bool do_move = true;
+	  if (surf2.get())
+	    {
+	      Vector2D uv = pts[ki]->getPar();
+	      if (surf2->inDomain(uv[0], uv[1], eps) &&
+		  dist >= pts[ki]->getSurfaceDist())
+		do_move = false;
+	    }
+	  if (do_move)
+	    {
+	      move.push_back(pts[ki]);
+	      vector<ftSamplePoint*> next = pts[ki]->getNeighbours();
+	      for (size_t kj=0; kj<next.size(); ++kj)
+		{
+		  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+		  if (curr->visited())
+		    continue;
+		  RevEngRegion *adj_reg = curr->region();
+		  if (adj_reg != adjacent)
+		    continue;
+		  curr->setVisited();
+		  pts.push_back(curr);
+		}
+	    }
+	}
+    }
+
+  for (size_t ki=0; ki<pts.size(); ++ki)
+    pts[ki]->unsetVisited();
+
+  if (move.size() > 0)
+    {
+      adjacent->removePoints(move);
+      adjacent->updateInfo(tol, angtol);
+
+      (void)addPointsToGroup(move, tol, angtol);
+    }
+      
+
+}
+
+//===========================================================================
+void RevEngRegion::growFromNeighbour(Point mainaxis[3], int min_pt_reg,
+				     vector<RevEngPoint*>& seed, double tol,
+				     double angtol, RevEngRegion *neighbour,
+				     bool do_update)
+//===========================================================================
+{
+  if (!hasSurface())
+    return;   // No growing is possible
+
+  bool check_pts = (neighbour->hasSurface());
+
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  shared_ptr<ElementarySurface> elem =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+  Point axis;
+  if (elem.get())
+    axis = elem->direction();
+  double axis_ang = 0.0;
+  if (surf->instanceType() == Class_Cylinder)
+    axis_ang = 0.5*M_PI;   // To be extended as appropriate
+  else if (surf->instanceType() == Class_Cone)
+    {
+      double cone_ang = ((Cone*)(elem.get()))->getConeAngle();
+      axis_ang = 0.5*M_PI - cone_ang;
+    }
+
+#ifdef DEBUG_GROWNEIGHBOUR
+  std::ofstream ofs1("seed1.g2");
+  ofs1 << "400 1 0 4 0 200 55 255" << std::endl;
+  ofs1 << seed.size() << std::endl;
+  for (size_t ki=0; ki<seed.size(); ++ki)
+    {
+      Vector3D xyz = seed[ki]->getPoint();
+      ofs1 << xyz << std::endl;
+    }
+#endif
+  
+  // Check initial points
+  double eps = 1.0e-6;
+  double tol2 = std::max(tol, 0.75*maxdist_);
+  tol2 = std::min(tol2, 2.0*tol);
+  double tol3 = 0.5*tol;
+  //double angtol2 = 0.5*angtol;
+  vector<RevEngPoint*> next_pts;
+  double upar, vpar, dist, ang, ang2;
+  Point close;
+  double dfac = 1.5;
+  for (size_t ki=0; ki<seed.size(); ++ki)
+    {
+      seed[ki]->setVisited();
+      Vector3D xyz = seed[ki]->getPoint();
+      Point norm = seed[ki]->getLocFuncNormal();
+      Point norm2 = seed[ki]->getTriangNormal();
+      surf->closestPoint(Point(xyz[0],xyz[1],xyz[2]), upar, vpar, close, dist, eps);
+      if (!elem.get())
+	surf->normal(axis, upar, vpar);
+      ang = axis.angle(norm);
+      ang = fabs(ang-axis_ang);
+      ang2 = axis.angle(norm2);
+      ang2 = fabs(ang2-axis_ang);
+      ang = std::min(std::min(ang, M_PI-ang), std::min(ang2, M_PI-ang2));
+      if (dist <= tol3 || (dist <= tol2 && ang <= angtol))
+	{
+	  if (check_pts)
+	    {
+	      double init_dist, init_ang;
+	      seed[ki]->getSurfaceDist(init_dist, init_ang);
+	      if ((dist < init_dist && ang < init_ang) ||
+		  (dfac*dist < init_dist && ang < dfac*init_ang))
+		next_pts.push_back(seed[ki]);
+	    }
+	  else
+	    next_pts.push_back(seed[ki]);
+	}
+    }
+
+#ifdef DEBUG_GROWNEIGHBOUR
+  std::ofstream of0("curr_region_from.g2");
+  neighbour->writeRegionPoints(of0);
+#endif
+
+#ifdef DEBUG_GROWNEIGHBOUR
+  std::ofstream ofs2("seed2.g2");
+  ofs2 << "400 1 0 4 0 200 55 255" << std::endl;
+  ofs2 << next_pts.size() << std::endl;
+  for (size_t ki=0; ki<next_pts.size(); ++ki)
+    {
+      Vector3D xyz = next_pts[ki]->getPoint();
+      ofs2 << xyz << std::endl;
+    }
+#endif
+  
+  // Grow
+  for (size_t ki=0; ki<next_pts.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next2 = next_pts[ki]->getNeighbours();
+      for (size_t kj=0; kj<next2.size(); ++kj)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next2[kj]);
+	  if (curr->visited())
+	    continue;
+	  RevEngRegion *adj_reg = curr->region();
+	  if (adj_reg != neighbour)
+	    continue;
+	  curr->setVisited();
+	  Vector3D xyz = curr->getPoint();
+	  Point norm = curr->getLocFuncNormal();
+	  Point norm2 = curr->getTriangNormal();
+	  surf->closestPoint(Point(xyz[0],xyz[1],xyz[2]), upar, vpar, close, dist, eps);
+	  if (!elem.get())
+	    surf->normal(axis, upar, vpar);
+	  ang = axis.angle(norm);
+	  ang = fabs(ang-axis_ang);
+	  ang2 = axis.angle(norm2);
+	  ang2 = fabs(ang2-axis_ang);
+	  ang = std::min(std::min(ang, M_PI-ang), std::min(ang2, M_PI-ang2));
+	  if (dist <= tol3 || (dist <= tol2 && ang <= angtol))
+	    {
+	      if (check_pts)
+		{
+		  double init_dist, init_ang;
+		  curr->getSurfaceDist(init_dist, init_ang);
+		  if ((dist < init_dist && ang < init_ang) ||
+		      (dfac*dist < init_dist && ang < dfac*init_ang))
+		    next_pts.push_back(curr);
+		}
+	      else
+		next_pts.push_back(curr);
+	    }
+	}
+    }
+  for (int ka=0; ka<neighbour->numPoints(); ++ka)
+    neighbour->getPoint(ka)->unsetVisited();
+
+  int classtype = surf->instanceType();
+  if (classtype == Class_Cylinder)
+    {
+      double dom[4];
+      Vector2D uv = group_points_[0]->getPar();
+      dom[0] = dom[1] = uv[0];
+      dom[2] = dom[3] = uv[1];
+      double dist, ang, avang;
+      double fac = 1.0/(double)group_points_.size();
+      group_points_[0]->getSurfaceDist(dist, ang);
+      avang = fac*ang;
+      for (size_t ki=1; ki<group_points_.size(); ++ki)
+	{
+	  Vector2D uv = group_points_[ki]->getPar();
+	  group_points_[ki]->getSurfaceDist(dist, ang);
+	  dom[0] = std::min(dom[0], uv[0]);
+	  dom[1] = std::max(dom[1], uv[0]);
+	  dom[2] = std::min(dom[2], uv[1]);
+	  dom[3] = std::max(dom[3], uv[1]);
+	  avang += fac*ang;
+	}
+      
+      vector<RevEngPoint*> adjpts;
+      vector<double> par_and_dist;
+      double avd, ava;
+      int nn;
+      getAdjInsideDist(surf, dom, tol, neighbour, avd, ava, nn, adjpts,
+		       par_and_dist);
+
+#ifdef DEBUG_GROWNEIGHBOUR2
+      std::ofstream of2("in_cyl_pts.g2");
+      of2 << "400 1 0 4 75 75 75 255" << std::endl;
+      of2 << adjpts.size() << std::endl;
+      for (size_t kh=0; kh<adjpts.size(); ++kh)
+	of2 << adjpts[kh]->getPoint() << std::endl;
+#endif
+      int stop_cyl = 1;
+    }
+  
+  // Move points
+  for (size_t ki=0; ki<next_pts.size(); ++ki)
+    {
+      //addPoint(next_pts[ki]);
+      neighbour->removePoint(next_pts[ki]);
+    }
+
+#ifdef DEBUG_GROWNEIGHBOUR2
+  std::ofstream of("updated_region_adj.g2");
+  writeRegionInfo(of);
+#endif
+  
+  // Update this region
+  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		  surf->instanceType() == Class_Cone);
+  double frac = 0.1;
+  int num_init = (int)group_points_.size();
+  if (next_pts.size() > 0)
+    {
+      // Compute distance
+      shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+      double maxdist, avdist;
+      int num_in, num2_in;
+      vector<pair<double, double> > dist_ang;
+      vector<double> parvals;
+      vector<RevEngPoint*> inpt, outpt; 
+      RevEngUtils::distToSurf(next_pts.begin(), next_pts.end(),
+			  surf, tol, maxdist, avdist, num_in, num2_in,
+			  inpt, outpt, parvals, dist_ang, angtol);
+
+      for (size_t kh=0; kh<next_pts.size(); ++kh)
+	{
+	  next_pts[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	  next_pts[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	  next_pts[kh]->setRegion(this);
+	}
+      group_points_.insert(group_points_.end(), next_pts.begin(),
+			   next_pts.end());
+      updateInfo(tol, angtol);
+      int sf_flag = defineSfFlag(0, tol, num_inside_,
+				 num_inside2_, avdist_, cyllike);
+      setSurfaceFlag(sf_flag);
+      if (do_update && surf_adaption_ == INITIAL &&
+	  (double)next_pts.size() > frac*(double)num_init)
+	checkReplaceSurf(mainaxis, min_pt_reg, tol, angtol);
+      for (size_t kj=0; kj<rev_edges_.size(); ++kj)
+	rev_edges_[kj]->increaseExtendCount();
+
+      neighbour->updateInfo(tol, angtol);
+    }
+
+  int stop_break = 1;
+}
+
+//===========================================================================
+vector<RevEngRegion*> RevEngRegion::commonAdjacent(RevEngRegion* adj_reg)
+//===========================================================================
+{
+  vector<RevEngRegion*> common;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      bool adjacent = (*it)->isAdjacent(adj_reg);
+      if (adjacent)
+	common.push_back(*it);
+    }
+  return common;
+}
+
+//===========================================================================
+vector<RevEngPoint*> RevEngRegion::extractBdPoints()
+//===========================================================================
+{
+  vector<RevEngPoint*> result;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next = group_points_[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	  RevEngRegion *adj_reg = curr->region();
+	  if (adj_reg != this)
+	    {
+	      result.push_back(group_points_[ki]);
+	      break;
+	    }
+	}
+    }
+  return result;
+}
+
+//===========================================================================
+vector<RevEngPoint*>
+RevEngRegion::extractBdPoints(vector<RevEngRegion*> regions)
+//===========================================================================
+{
+  vector<RevEngPoint*> result;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next = group_points_[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	  RevEngRegion *adj_reg = curr->region();
+	  if (adj_reg != this)
+	    {
+	      size_t kr;
+	      for (kr=0; kr<regions.size(); ++kr)
+		if (adj_reg == regions[kr])
+		  break;
+
+	      if (kr < regions.size())
+		result.push_back(group_points_[ki]);
+	      break;
+	    }
+	}
+    }
+  return result;
+}
+
+//===========================================================================
+vector<RevEngPoint*> RevEngRegion::extractBranchPoints()
+//===========================================================================
+{
+  vector<RevEngPoint*> result;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      int num_other = 0;
+      RevEngRegion *other = 0;
+      vector<ftSamplePoint*> next = group_points_[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	  RevEngRegion *adj_reg = curr->region();
+	  if (adj_reg != this)
+	    {
+	      if (num_other == 0)
+		{
+		  num_other++;
+		  other = adj_reg;
+		}
+	      else if (adj_reg != other)
+		{
+		  result.push_back(group_points_[ki]);
+		  break;
+		}
+	    }
+	}
+    }
+  return result;
+}
+
+//===========================================================================
+vector<RevEngPoint*> RevEngRegion::extractNextToAdjacent(RevEngRegion* reg)
+//===========================================================================
+{
+  vector<RevEngPoint*> result;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next = group_points_[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	  RevEngRegion *adj_reg = curr->region();
+	  if (adj_reg == reg)
+	    {
+	      result.push_back(group_points_[ki]);
+	      break;
+	    }
+	}
+    }
+  return result;
+}
+
+struct DistanceInfo
+{
+  double mind_, maxd_, avd_;
+  double pmin_, pmax_;
+  int nmb_;
+
+  DistanceInfo()
+  {
+    mind_ = maxd_ = avd_ = pmin_ = pmax_ = 0.0;
+    nmb_ = 0;
+  }
+  
+  DistanceInfo(double mind, double maxd, double avd,
+	       double pmin, double pmax, int nmb)
+  {
+    mind_ = mind;
+    maxd_ = maxd;
+    avd_ = avd;
+    pmin_ = pmin;
+    pmax_ = pmax;
+    nmb_ = nmb;
+  }
+
+  void setInfo(double mind, double maxd, double avd,
+	       double pmin, double pmax, int nmb)
+  {
+    mind_ = mind;
+    maxd_ = maxd;
+    avd_ = avd;
+    pmin_ = pmin;
+    pmax_ = pmax;
+    nmb_ = nmb;
+  }
+};
+
+void RevEngRegion::testBlendGap(vector<shared_ptr<CurveOnSurface> >& cvs,
+				double tmin, double tmax, double tdel, double width,
+				vector<pair<double,double> >& not_gap)
+{
+  double cdel0 = tmax - tmin;
+  int ncheck = std::max((int)(1.5*cdel0/tdel), 1);
+  double cdel = cdel0/(double)ncheck;
+  double cpar;
+  size_t kr;
+  vector<size_t> inside;
+  for (kr=0, cpar=tmin+0.5*cdel; kr<ncheck; ++kr, cpar+=cdel)
+    {
+      size_t kh;
+      for (kh=0; kh<cvs.size(); ++kh)
+	if (cvs[kh]->startparam() <= cpar && cvs[kh]->endparam() >= cpar)
+	  break;
+      if (kh >= cvs.size())
+	{
+	  std::cout << cvs.size() << " " << kh << std::endl;
+	}
+      Point cpt = cvs[kh]->ParamCurve::point(cpar);
+      double dist;
+      RevEngPoint* rpt = closestPoint(cpt, dist);
+      if (dist <= width)
+	inside.push_back(kr);
+      else
+	{
+	  Vector3D xyz = rpt->getPoint();
+	  Point rpt2(xyz[0], xyz[1], xyz[2]);
+	  double tpar2, dist2;
+	  Point close;
+	  cvs[kh]->closestPoint(rpt2, std::max(cvs[kh]->startparam(),cpar-0.25*cdel),
+				std::min(cvs[kh]->endparam(),cpar+0.25*cdel),
+				tpar2, close, dist2);
+	  if (dist2 <= width)
+	    inside.push_back(kr);
+	}
+    }
+  if ((int)inside.size() > ncheck/2)
+    not_gap.push_back(std::make_pair(tmin, tmax));
+  else if (inside.size() > 0)
+    {
+      double t1 = tmin + inside[0]*cdel;
+      double t2 = t1 + cdel;
+      for (size_t kj=1; kj<inside.size(); ++kj)
+	{
+	  if (inside[kj] == inside[kj-1]+1)
+	    t2 += cdel;
+	  else
+	    {
+	      not_gap.push_back(std::make_pair(t1, t2));
+	      t1 = tmin + inside[kj]*cdel;
+	      t2 = t1 + cdel;
+	    }
+	}
+      not_gap.push_back(std::make_pair(t1, t2));
+    }
+}
+
+//===========================================================================
+void RevEngRegion::estimateBlendDimensions(vector<shared_ptr<CurveOnSurface> >& cvs,
+					   vector<RevEngPoint*>& bd_points,
+					   double tol, double distlim,
+					   vector<pair<double,double> >& t1_t2,
+					   vector<double>& width, int& num_in_lim)
+//===========================================================================
+{
+  // if (cvs.size() != 1)
+  //   return;  // Must consider this later. Will it happen?
+  
+  // Parameterize points with respect to the given curve
+  double eps = 1.0e-9;
+  vector<double> param(bd_points.size());
+  vector<double> distance(bd_points.size());
+  
+  double tmin = cvs[0]->startparam();
+  double tmax = cvs[cvs.size()-1]->endparam();
+  num_in_lim = 0;
+  for (size_t ki=0; ki<bd_points.size(); ++ki)
+    {
+      double tpar, dist=std::numeric_limits<double>::max();;
+      Vector3D xyz = bd_points[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      for (size_t kj=0; kj<cvs.size(); ++kj)
+	{
+	  double tpar0, dist0;
+	  Point close;
+	  cvs[kj]->closestPoint(pt, cvs[kj]->startparam(), cvs[kj]->endparam(),
+				tpar0, close, dist0);
+	  if (dist0 < dist)
+	    {
+	      tpar = tpar0;
+	      dist = dist0;
+	    }
+	}
+      param[ki] = tpar;
+      distance[ki] = dist;
+      tmin = std::min(tmin, tpar);
+      tmax = std::max(tmax, tpar);
+      if (dist <= distlim)
+	num_in_lim++;
+    }
+
+  if (tmax < tmin+eps)
+    return;
+  
+  // Sort
+  for (size_t ki=0; ki<bd_points.size(); ++ki)
+    for (size_t kj=ki+1; kj<bd_points.size(); ++kj)
+      if (param[kj] < param[ki])
+	{
+	  std::swap(bd_points[ki], bd_points[kj]);
+	  std::swap(param[ki], param[kj]);
+	  std::swap(distance[ki], distance[kj]);
+	}
+
+  // Limit point sequence
+  size_t first = 0, last = param.size()-1;
+  eps = std::min(1.0e-9, 1.0e-6*(tmax-tmin));
+  for (; first<last; ++first)
+    if (param[first+1]-param[0] > eps)
+      break;
+  for (; last>first; --last)
+    if (param[param.size()-1]-param[last-1] > eps)
+      break;
+
+  double mean_out = 0.0;
+  double min_out = std::numeric_limits<double>::max();
+  int nmb_out = 0;
+  size_t outlim = distance.size()/10;
+  for (size_t ki=0; ki<std::max(first,outlim); ++ki)
+    {
+      min_out = std::min(min_out, distance[ki]);
+      mean_out += distance[ki];
+      nmb_out++;
+    }
+  for (size_t ki=std::min(distance.size()-outlim,last+1);
+       ki<distance.size(); ++ki)
+    {
+      min_out = std::min(min_out, distance[ki]);
+      mean_out += distance[ki];
+      nmb_out++;
+    }
+  if (nmb_out > 0)
+    mean_out /= (double)nmb_out;
+  else
+    min_out = 10.0*distlim;
+  
+  // Find start point for search
+  double max_dist = 0.0;
+  double min_dist = std::numeric_limits<double>::max();
+  int min_ix = -1;
+  for (size_t ki=first; ki<=last; ++ki)
+    {
+      if (distance[ki] < min_dist)
+	{
+	  min_dist = distance[ki];
+	  min_ix = (int)ki;
+	}
+      max_dist = std::max(max_dist,distance[ki]);
+    }
+
+  int ndel = std::max(20, (int)(last-first)/100);
+  ndel = std::max(1, std::min(ndel, (int)(last-first)/5));
+  vector<DistanceInfo> distinfo(ndel);
+  double dlim = std::max(2.0*distlim, 0.5*(min_out + mean_out));
+  double tdel = (tmax-tmin)/(double)ndel;
+  double tpar;
+  int ka, kb, ix;
+  for (ix=0, ka=first, tpar=tmin+tdel; ix<ndel && ka<=(int)last;
+       ++ix, ka=kb, tpar+=tdel)
+    {
+      int nmb = 0;
+      double maxd=0.0, avd=0.0;
+      double mind = 2.0*dlim;
+      for (kb=ka; kb<=(int)last && param[kb]<=tpar; ++kb)
+	{
+	  if (distance[kb]>dlim)
+	    continue;
+	  ++nmb;
+	  maxd = std::max(maxd, distance[kb]);
+	  mind = std::min(mind, distance[kb]);
+	  avd += distance[kb];
+	}
+      if (nmb > 0)
+	avd /= (double)nmb;
+      
+      distinfo[ix].setInfo(mind, maxd, avd, tpar-tdel, tpar, nmb);
+    }
+  
+  double del = 0.0;
+  for (size_t ki=0; ki<distinfo.size(); ++ki)
+    del += (distinfo[ki].avd_ - distinfo[ki].mind_);
+  del /= (double)(distinfo.size());
+  del = std::min(del, mean_out);
+
+  size_t kj, kr;
+  int pt_out_lim = 5;
+  double eps2 = 0.01*(mean_out - min_dist);
+  for (kj=0; kj<distinfo.size(); kj=kr+1)
+    {
+      double curr_width = 0.0;
+      double tmin2 = distinfo[kj].pmin_;
+      double tmax2 = distinfo[kj].pmax_;
+      int num_pt = 0;
+      double min_width = max_dist;
+      for (kr=kj; kr<distinfo.size(); ++kr)
+	{
+	  if (distinfo[kr].nmb_ == 0)
+	    break;
+	  if (distinfo[kr].mind_ > distlim)
+	    break;
+	  num_pt += distinfo[kr].nmb_;
+
+	  min_width = std::min(min_width, distinfo[kr].mind_);
+	  curr_width = std::max(curr_width, std::min(distinfo[kr].maxd_,
+						     distinfo[kr].mind_+del));
+	  tmax2 = distinfo[kr].pmax_;
+	}
+      if (num_pt > 0 && min_width <= distlim)
+	{
+	  curr_width = std::min(curr_width, mean_out + 0.1*(max_dist-mean_out));
+	  curr_width = std::max(curr_width, 0.2*distlim);
+	  curr_width = std::max(curr_width, min_width+eps2);
+	  t1_t2.push_back(std::make_pair(tmin2, tmax2));
+	  width.push_back(curr_width);
+	}
+    }
+
+  // The boundary points may be too sparse. Check if the gaps between
+  // curve pieces are real
+  if (t1_t2.size() > 0)
+    {
+      size_t nmb = t1_t2.size();
+      vector<pair<double,double> > t3_t4;
+      vector<double> width2;
+      if (t1_t2[0].first > tmin+eps)
+	{
+	  vector<pair<double,double> > not_gap;
+	  testBlendGap(cvs, tmin, t1_t2[0].first, tdel, width[0], not_gap);
+	  if (not_gap.size() > 0)
+	    {
+	      t3_t4.insert(t3_t4.end(), not_gap.begin(), not_gap.end());
+	      for (size_t kh=0; kh<not_gap.size(); ++kh)
+		width2.push_back(width[0]);
+	    }
+	}
+      t3_t4.push_back(t1_t2[0]);
+      width2.push_back(width[0]);
+      for (size_t kj=1; kj<t1_t2.size(); ++kj)
+	{
+	  vector<pair<double,double> > not_gap;
+	  double wdt = 0.5*(width[kj-1]+width[kj]);
+	  testBlendGap(cvs, t1_t2[kj-1].second, t1_t2[kj].first, tdel, wdt, not_gap);
+	  if (not_gap.size() > 0)
+	    {
+	      t3_t4.insert(t3_t4.end(), not_gap.begin(), not_gap.end());
+	      for (size_t kh=0; kh<not_gap.size(); ++kh)
+		width2.push_back(wdt);
+	    }
+	  t3_t4.push_back(t1_t2[kj]);
+	  width2.push_back(width[kj]);
+	}
+     if (tmax > t1_t2[nmb-1].second)
+	{
+	  vector<pair<double,double> > not_gap;
+	  testBlendGap(cvs, t1_t2[nmb-1].second, tmax, tdel, width[nmb-1], not_gap);
+	  if (not_gap.size() > 0)
+	    {
+	      t3_t4.insert(t3_t4.end(), not_gap.begin(), not_gap.end());
+	      for (size_t kh=0; kh<not_gap.size(); ++kh)
+		width2.push_back(width[nmb-1]);
+	    }
+	}
+      if (t3_t4.size() > t1_t2.size())
+	{
+	  double ltol = 0.5*tdel;
+	  for (int ka=(int)t3_t4.size()-1; ka>0; --ka)
+	    {
+	      if (t3_t4[ka].first - t3_t4[ka-1].second <= ltol)
+		{
+		  t3_t4[ka-1].second = t3_t4[ka].second;
+		  width2[ka-1] = 0.5*(width2[ka-1] + width2[ka]);
+		  t3_t4.erase(t3_t4.begin()+ka);
+		  width2.erase(width2.begin()+ka);
+		}
+	    }
+	  std::swap(t1_t2, t3_t4);
+	  std::swap(width, width2);
+	}
+    }
+  
+  int stop_break = 1;
+}
+
+
+//===========================================================================
+void RevEngRegion::getNearPoints(shared_ptr<CurveOnSurface>& cv,
+				 double& tmin, double& tmax,
+				 double width, double angtol, 
+				 vector<RevEngPoint*>& nearpoints)
+//===========================================================================
+{
+  // if (cvs.size() != 1)
+  //   return;  // Must consider this later. Will it happen?
+
+  double pihalf = 0.5*M_PI;
+  double tmin2 = tmax;
+  double tmax2 = tmin;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      double tpar, dist=std::numeric_limits<double>::max();
+      Point close;
+      Vector3D xyz = group_points_[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      cv->closestPoint(pt, cv->startparam(), cv->endparam(),
+		       tpar, close, dist);
+
+      vector<Point> der(2);
+      cv->point(der, tpar, 1);
+      Point vec = pt - close;
+      double ang = vec.angle(der[1]);
+      Point norm1 = group_points_[ki]->getLocFuncNormal();
+      Point norm2 = group_points_[ki]->getTriangNormal();
+      double ang1 = norm1.angle(der[1]);
+      double ang2 = norm2.angle(der[1]);
+      ang1 = std::min(ang1, M_PI-ang1);
+      ang2 = std::min(ang2, M_PI-ang2);
+      if (tpar > tmin && tpar < tmax && dist <= width &&
+	  fabs(pihalf-ang) <= angtol && std::min(ang1,ang2) > angtol)
+	{
+	  nearpoints.push_back(group_points_[ki]);
+	  tmin2 = std::min(tmin2, tpar);
+	  tmax2 = std::max(tmax2, tpar);
+	}
+    }
+  tmin = tmin2;
+  tmax = tmax2;
+}
+
+//===========================================================================
+void RevEngRegion::getNearPoints2(vector<RevEngPoint*>& points,
+				  shared_ptr<CurveOnSurface>& cv,
+				  double width,
+				  vector<RevEngPoint*>& nearpoints)
+//===========================================================================
+{
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      double tpar, dist;
+      Point close;
+      cv->closestPoint(pt, cv->startparam(), cv->endparam(),
+		       tpar, close, dist);
+
+      if (dist <= width)
+	nearpoints.push_back(points[ki]);
+    }
+}
+
+  
+  
+//===========================================================================
+vector<RevEngPoint*>
+RevEngRegion::removeOutOfSurf(vector<RevEngPoint*>& points,
+			      double tol, double angtol, bool outer,
+			      double& min_dist)
+//===========================================================================
+{
+  vector<RevEngPoint*> result;
+  double diag = bbox_.low().dist(bbox_.high());
+  min_dist = 10.0*diag;
+  if (!hasSurface())
+    return result;
+
+  int code;
+  int classtype = getSurface(0)->instanceType(code);
+  if (classtype != Class_Plane && classtype != Class_Cylinder &&
+      classtype != Class_Cone)
+    return result;  // For the time being
+
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  shared_ptr<ElementarySurface> elem =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+
+  Point vec = elem->direction();
+  Point loc = elem->location();
+  double rad = elem->radius(0.0, 0.0);
+  double alpha = 0.0;
+  if (elem->instanceType() == Class_Cone)
+    {
+      shared_ptr<Cone> cone = dynamic_pointer_cast<Cone,ElementarySurface>(elem);
+      alpha = cone->getConeAngle();
+    }
+
+  // Check orientation. Apply to plane
+  bool plane = (elem->instanceType() == Class_Plane);
+  if (plane)
+    {
+      Point normal = group_points_[0]->getTriangNormal();
+      if (vec*normal < 0.0)
+	vec *= -1;
+      if (!outer)
+	vec *= -1;
+    }
+
+  double upar, vpar, dist;
+  Point close;
+  double eps = 1.0e-6;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      if (plane)
+	{
+	  elem->closestPoint(pt, upar, vpar, close, dist, eps);
+	  if (dist < tol || (pt-close)*vec > 0.0)
+	    {
+	      min_dist = std::min(min_dist, dist);
+	      result.push_back(points[ki]);
+	    }
+	}
+      else
+	{
+	  double vval = (pt-loc)*vec;
+	  Point pt2 = pt - vval*vec;
+	  dist = loc.dist(pt2);
+	  double dist2 = vval*tan(alpha);
+	  dist -= dist2;
+	  if ((outer && dist < rad) || (!outer && dist > rad))
+	    {
+	      min_dist = std::min(min_dist, dist);
+	      result.push_back(points[ki]);
+	    }
+	}
+    }
+  return result;
+}
+
+//===========================================================================
+bool
+RevEngRegion::analyseCylinderContext(vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj,
+				     double tol, double angtol, Point mainaxis[3], int mode,
+				     vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj_planar,
+				     Point& pos, Point& axis, Point& Cx, double& rad,
+				     vector<RevEngPoint*>& cyl_pts)
+//===========================================================================
+{
+  for (size_t ki=0; ki<adj.size(); ++ki)
+    {
+      if (adj[ki].first->instanceType() == Class_Plane)
+	adj_planar.push_back(adj[ki]);
+      if (mode > 1 && adj[ki].first->instanceType() == Class_Cylinder)
+	adj_planar.push_back(adj[ki]);
+    }
+  
+  if (adj_planar.size() < 2)
+    return false;
+  
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    for (size_t kj=ki+1; kj<adj_planar.size(); ++kj)
+      if (adj_planar[kj].second->numPoints() > adj_planar[ki].second->numPoints())
+	std::swap(adj_planar[ki], adj_planar[kj]);
+
+#ifdef DEBUG_CYLCONTEXT
+  std::ofstream ofadj("adj_planes.g2");
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    {
+      adj_planar[ki].first->writeStandardHeader(ofadj);
+      adj_planar[ki].first->write(ofadj);
+    }
+#endif
+
+  // Find feasible axis directions
+  vector<Point> dir;
+  double acute_lim = 0.25*M_PI;
+  for (size_t ki=0; ki<adj_planar.size(); ++ki)
+    {
+      Point dir1 = adj_planar[ki].first->direction();
+      if (mode > 1)
+	dir.push_back(dir1);
+      for (size_t kj=ki+1; kj<adj_planar.size(); ++kj)
+	{
+	  Point dir2 = adj_planar[kj].first->direction();
+	  double ang = dir1.angle(dir2);
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang > angtol && ang < acute_lim)
+	    continue;
+
+	  if (mode == 1 && ang <= angtol)
+	    {
+	      if (dir1*dir2 < 0.0)
+		dir2 *= -1;
+	      dir.push_back(0.5*(dir1 + dir2));
+	    }
+	  if (ang > angtol)
+	    dir.push_back(dir1.cross(dir2));
+	}
+    }
+
+  // Clean up in directions
+  for (size_t ki=0; ki<dir.size(); ++ki)
+    for (size_t kj=ki+1; kj<dir.size(); )
+      {
+	double ang = dir[ki].angle(dir[kj]);
+	ang = std::min(ang, M_PI-ang);
+	if (ang < angtol)
+	  dir.erase(dir.begin()+kj);
+	else
+	  ++kj;
+      }
+  if (dir.size() < 1)
+    return false;
+
+  // Find best candidate direction
+  double ang_lim = 0.1*M_PI;
+  double pihalf = 0.5*M_PI;
+  for (size_t ki=0; ki<dir.size(); ++ki)
+    dir[ki].normalize();
+
+  // Check feasability of cylinder
+  vector<vector<RevEngPoint*> > dir_pts(dir.size());
+  for (size_t kj=0; kj<group_points_.size(); ++kj)
+    {
+      Point norm = group_points_[kj]->getLocFuncNormal();
+      Point norm2 = group_points_[kj]->getTriangNormal();
+      for (size_t ki=0; ki<dir.size(); ++ki)
+	{
+	  double ang = dir[ki].angle(norm);
+	  double ang2 = dir[ki].angle(norm2);
+	  if (std::min(fabs(pihalf-ang2), fabs(pihalf-ang)) < ang_lim)
+	    dir_pts[ki].push_back(group_points_[kj]);
+	}
+    }
+
+  vector<double> MAH(dir.size(), 0.0);
+  vector<double> MAK(dir.size(), 0.0);
+  for (size_t ki=0; ki<dir.size(); ++ki)
+    {
+      for (size_t kj=0; kj<dir_pts[ki].size(); ++kj)
+	{
+	  MAK[ki] += fabs(dir_pts[ki][kj]->GaussCurvature());
+	  MAH[ki] += fabs(dir_pts[ki][kj]->meanCurvature());
+	}
+      MAK[ki] /= (double)dir_pts[ki].size();
+      MAH[ki] /= (double)dir_pts[ki].size();
+    }
+
+  int min_frac = std::numeric_limits<double>::max();
+  int ix = -1;
+  double eps = 1.0e-9;
+  double num_frac = 0.25;
+  double min_num = num_frac*(double)group_points_.size();
+  for (size_t ki=0; ki<dir.size(); ++ki)
+    {
+      if ((double)dir_pts[ki].size() < min_num)
+	continue;
+      double frac = (MAH[ki]<eps) ? 1.0e8 : MAK[ki]/MAH[ki];
+      if (frac < min_frac)
+	{
+	  min_frac = frac;
+	  ix = (int)ki;
+	}
+    }
+
+  if (ix < 0)
+    return false;
+
+#ifdef DEBUG_CYLCONTEXT
+  std::ofstream of2("cyl_cand_pts.g2");
+  of2 << "400 1 0 4 0 255 0 255" << std::endl;
+  of2 << dir_pts[ix].size() << std::endl;
+  for (size_t kj=0; kj<dir_pts[ix].size(); ++kj)
+    of2 << dir_pts[ix][kj]->getPoint() << std::endl;
+#endif
+
+  vector<vector<RevEngPoint*> > conn_groups;
+  vector<RevEngPoint*> dummy;
+  connectedGroups(dir_pts[ix], conn_groups, false, dummy);
+  int min_conn = 0;
+  int conn_ix = -1;
+  for (size_t ki=0; ki<conn_groups.size(); ++ki)
+    if ((int)conn_groups[ki].size() > min_conn)
+      {
+	min_conn = (int)conn_groups[ki].size();
+	conn_ix = (int)ki;
+      }
+  
+  if ((double)conn_groups[conn_ix].size() >= min_num)
+    {
+      // Candidate found
+      // Compute cylinder centre and radius. Project points
+      axis = dir[ix];
+      vector<pair<vector<RevEngPoint*>::iterator,
+		  vector<RevEngPoint*>::iterator> > group;
+      group.push_back(std::make_pair(conn_groups[conn_ix].begin(),
+				     conn_groups[conn_ix].end()));
+      Point mid = 0.5*(bbox_.low() + bbox_.high());
+      vector<Point> projected;
+      double maxdp, avdp;
+      RevEngUtils::projectToPlane(group, axis, mid, projected, maxdp, avdp);
+      
+      int min_ix = -1;
+      double min_ang = M_PI;
+      for (int ka=0; ka<3; ++ka)
+	{
+	  double ang = mainaxis[ka].angle(axis);
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang < min_ang)
+	    {
+	      min_ix = ka;
+	      min_ang = ang;
+	    }
+	}
+      Cx = mainaxis[(min_ix+1)%3];
+      Point Cy = axis.cross(Cx);
+      Cy.normalize();
+      Cx = Cy.cross(axis);
+      Cx.normalize();
+
+      RevEngUtils::computeCircPosRadius(projected, axis, Cx, Cy, pos, rad);
+
+      cyl_pts.insert(cyl_pts.end(), conn_groups[conn_ix].begin(),
+		     conn_groups[conn_ix].end());
+      return true;
+    }
+
+  return false;
+}
+
+//===========================================================================
+bool
+RevEngRegion::identifySignificantAxis(vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj,
+				      Point& pos, Point& axis, Point& axis2)
+//===========================================================================
+{
+  // Currently applicable only for point groups with an associated plane
+  if (!hasSurface())
+    return false;
+  int code;
+  int classtype = getSurface(0)->instanceType(code);
+  if (classtype != Class_Plane && classtype != Class_Sphere)
+    return false;
+
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  shared_ptr<ElementarySurface> elem =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+  if (!elem.get())
+    return false;  // Should not happen
+
+  Point norm = elem->direction();
+  double ang1 = norm.angle(normalcone_.centre());
+  double ang2 = norm.angle(normalcone2_.centre());
+  double ang3 = norm.angle(avnorm_);
+  double ang4 = norm.angle(avnorm2_);
+  double anglim = 0.1*M_PI;
+  if (ang1 > anglim && ang2 > anglim && ang3 > anglim && ang4 > anglim)
+    return false;   // Surface orientation is incompatible with point cloud
+
+  double min_ang = M_PI;
+  for (size_t ki=0; ki<adj.size(); ++ki)
+    {
+      if (adj[ki].first->instanceType() != Class_Cylinder &&
+	  adj[ki].first->instanceType() != Class_Cone &&
+	  adj[ki].first->instanceType() != Class_Torus)
+	continue;
+      if (adj[ki].second->getSurfaceFlag() >= ACCURACY_POOR)
+	continue;
+      Point dir = adj[ki].first->direction();
+      double ang = norm.angle(dir);
+      ang = std::min(M_PI,ang);
+      if (ang < min_ang)
+	{
+	  min_ang = ang;
+	  axis = dir;
+	  axis2 = adj[ki].first->direction2();
+	  pos = adj[ki].first->location();
+	}
+    }
+
+  if (axis.dimension() == 0 || min_ang > anglim)
+    return false;
+  else
+    return true;
+}
+
+
+//===========================================================================
+void
+RevEngRegion::analyseRotated(Point& pos, Point& axis, Point& axis2)
+//===========================================================================
+{
+  
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  RevEngUtils::rotateToPlane(group, axis2, axis, pos, rotated);
+
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  double len = low.dist(high);
+
+#ifdef DEBUG_VALIDATE
+  std::ofstream of("rotated_points.g2");
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << rotated.size() << std::endl;
+  for (size_t kr=0; kr<rotated.size(); ++kr)
+    of << rotated[kr] << std::endl;
+  of << "410 1 0 4 0 0 255 255" << std::endl;
+  of << "1" << std::endl;
+  of << pos-0.5*len*axis << " " << pos+0.5*len*axis << std::endl;
+  of << "410 1 0 4 0 255 0 255" << std::endl;
+  of << "1" << std::endl;
+  of << pos-0.5*len*axis2 << " " << pos+0.5*len*axis2 << std::endl;
+#endif
+
+  int stop_break = 1;
+}
+
+//===========================================================================
+shared_ptr<Torus>
+RevEngRegion::analyseRotatedTorus(Point& pos, Point& Cx, Point& axis,
+				  double tol, double angtol)
+//===========================================================================
+{
+  // Compute minor circle of torus given rotational axis
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(group_points_.begin(), group_points_.end()));
+  RevEngUtils::rotateToPlane(group, Cx, axis, pos, rotated);
+
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  double len = low.dist(high);
+
+#ifdef DEBUG_EXTRACT
+  std::ofstream of("rotated_points_tor.g2");
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << rotated.size() << std::endl;
+  for (size_t kr=0; kr<rotated.size(); ++kr)
+    of << rotated[kr] << std::endl;
+  of << "410 1 0 4 0 0 255 255" << std::endl;
+  of << "1" << std::endl;
+  of << pos-0.5*len*axis << " " << pos+0.5*len*axis << std::endl;
+  of << "410 1 0 4 0 255 0 255" << std::endl;
+  of << "1" << std::endl;
+  of << pos-0.5*len*Cx << " " << pos+0.5*len*Cx << std::endl;
+#endif
+
+  Point cpos;
+  double crad;
+  Point Cy = axis.cross(Cx);
+  RevEngUtils::computeCircPosRadius(rotated, Cy, Cx, axis, cpos, crad);
+  shared_ptr<Circle> circ(new Circle(crad, cpos, Cy, Cx));
+#ifdef DEBUG_EXTRACT
+  circ->writeStandardHeader(of);
+  circ->write(of);
+#endif
+  Point cvec = cpos - pos;
+  double R1 = (cvec - (cvec*axis)*axis).length();
+  double R2 = (cvec*axis)*axis.length();
+  shared_ptr<Torus> tor(new Torus(R1, crad, pos+R2*axis, axis, Cy));
+  
+#ifdef DEBUG_EXTRACT
+  tor->writeStandardHeader(of);
+  tor->write(of);
+#endif
+
+  // Check accuracy
+  double maxd, avd;
+  int num, num2;
+  vector<RevEngPoint*> in, out;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  tor, tol, maxd, avd, num, num2, in, out,
+			  parvals, dist_ang, angtol);
+  int sf_flag = defineSfFlag(0, tol, num, num2, avd, false);
+  if (sf_flag == ACCURACY_OK)
+    {
+      // Finished
+      return tor;
+    }
+
+  vector<RevEngPoint*> cyl_pts1, cyl_pts2, remaining1, remaining2;
+  int sgn = (avH_ < 0.0) ? 1 : -1;
+  RevEngUtils::extractLinearPoints(group_points_, rotated,
+				   len, pos, axis, R1+sgn*crad, axis, false,
+				   tol, angtol, dist_ang,
+				   cyl_pts1, true, remaining1);
+  RevEngUtils::extractLinearPoints(group_points_, rotated,
+				   len, pos, axis, R1+sgn*crad, axis, false,
+				   tol, angtol, dist_ang,
+				   cyl_pts2, false, remaining2);
+
+#ifdef DEBUG_EXTRACT
+  if (cyl_pts1.size() > 0)
+    {
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of << cyl_pts1.size() << std::endl;
+      for (size_t ki=0; ki<cyl_pts1.size(); ++ki)
+	of << cyl_pts1[ki]->getPoint() << std::endl;
+    }
+  if (cyl_pts2.size() > 0)
+    {
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of << cyl_pts2.size() << std::endl;
+      for (size_t ki=0; ki<cyl_pts2.size(); ++ki)
+	of << cyl_pts2[ki]->getPoint() << std::endl;
+    }
+#endif
+
+  vector<RevEngPoint*> distpt, angpt;
+  identifyDistPoints(dist_ang, tol, maxd, avd, distpt);
+  identifyAngPoints(dist_ang, angtol, tol, angpt);
+#ifdef DEBUG_EXTRACT
+  std::ofstream of2("dist_ang_out.g2");
+  if (distpt.size() > 0)
+    {
+      of2 << "400 1 0 4 55 200 0 255" << std::endl;
+      of2 << distpt.size() << std::endl;
+      for (size_t ki=0; ki<distpt.size(); ++ki)
+	of2 << distpt[ki]->getPoint() << std::endl;
+    }
+  if (angpt.size() > 0)
+    {
+      of2 << "400 1 0 4 55 0 200 255" << std::endl;
+      of2 << angpt.size() << std::endl;
+      for (size_t ki=0; ki<angpt.size(); ++ki)
+	of2 << angpt[ki]->getPoint() << std::endl;
+    }
+#endif
+  int stop_break = 1;
+  return tor;
+}
+
+//===========================================================================
+bool
+RevEngRegion::analyseTorusContext(vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> >& adj,
+				  double tol, double angtol, vector<size_t>& adjacent_ix,
+				  int &plane_ix, int& cyl_ix,
+				  Point& pos, Point& axis, Point& Cx, double& R1,
+				  double& R2, double dom[4], bool& outer,
+				  bool& analyse_rotated)
+//===========================================================================
+{
+  for (size_t ki=0; ki<adj.size(); ++ki)
+    for (size_t kj=ki+1; kj<adj.size(); ++kj)
+      if (adj[kj].second->numPoints() > adj[ki].second->numPoints())
+	std::swap(adj[ki], adj[kj]);
+
+  analyse_rotated = false;
+  plane_ix=-1;
+  cyl_ix=-1;
+  //vector<Point> dir;
+  vector<pair<size_t, size_t> > combo;
+  vector<pair<int, int> > num_common;
+  vector<double> angle;
+  for (size_t ki=0; ki<adj.size(); ++ki)
+    {
+      shared_ptr<ElementarySurface> elem = adj[ki].first;
+      if (elem->instanceType() != Class_Plane)
+	continue;
+      Point plane_axis = elem->direction();
+       for (size_t kj=0; kj<adj.size(); ++kj)
+	{
+	  shared_ptr<ElementarySurface> elem2 = adj[kj].first;
+	  if (elem2->instanceType() != Class_Cylinder)
+	    continue;
+	  Point cyl_axis = elem2->direction();
+	  double ang = plane_axis.angle(cyl_axis);
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang < angtol)
+	    {
+	      combo.push_back(std::make_pair(ki, kj));
+	      vector<RevEngPoint*> adj_pt1 = extractNextToAdjacent(adj[ki].second);
+	      vector<RevEngPoint*> adj_pt2 = extractNextToAdjacent(adj[kj].second);
+	      num_common.push_back(std::make_pair((int)adj_pt1.size(), (int)adj_pt2.size()));
+	      angle.push_back(ang);
+	    }
+	}
+    }
+     
+  // for (size_t ki=0; ki<adj.size(); ++ki)
+  //   {
+  //     shared_ptr<ElementarySurface> elem = adj[ki].first;
+  //     if (!(elem->instanceType() == Class_Plane || elem->instanceType() == Class_Cylinder))
+  // 	continue;
+
+  //     Point curr_dir = elem->direction();
+  //     dir.push_back(curr_dir);
+  //     adjacent_ix.push_back(ki);
+  //     if (elem->instanceType() == Class_Plane)
+  // 	{
+  // 	  plane = true;
+  // 	  plane_ix = (int)ki;
+  // 	}
+  //     else
+  // 	{
+  // 	  cyl = true;
+  // 	  pos = elem->location();
+  // 	  Cx = elem->direction2();
+  // 	  radius = elem->radius(0.0, 0.0);
+  // 	  cyl_ix = (int)ki;
+  // 	}
+
+  //     for (size_t kj=ki+1; kj<adj.size(); ++kj)
+  // 	{
+  // 	  if (!(adj[kj].first->instanceType() == Class_Plane ||
+  // 		adj[kj].first->instanceType() == Class_Cylinder))
+  // 	    continue;
+  // 	  curr_dir = adj[kj].first->direction();
+  // 	  double ang = dir[0].angle(curr_dir);
+  // 	  ang = std::min(ang, M_PI-ang);
+  // 	  if (ang < angtol)
+  // 	    {
+  // 	      dir.push_back(curr_dir);
+  // 	      adjacent_ix.push_back(kj);
+  // 	      if (adj[kj].first->instanceType() == Class_Plane)
+  // 		{
+  // 		  plane = true;
+  // 		  if (plane_ix < 0)
+  // 		    plane_ix = (int)kj;
+  // 		}
+  // 	      else
+  // 		{
+  // 		  if (!cyl)
+  // 		    {
+  // 		      pos = adj[kj].first->location();
+  // 		      Cx = adj[kj].first->direction2();
+  // 		      radius = adj[kj].first->radius(0.0, 0.0);
+  // 		      cyl_ix = (int)kj;
+  // 		    }
+  // 		  cyl = true;
+  // 		}
+  // 	    }
+  // 	}
+
+  //     if (plane && cyl)
+  // 	{
+  // 	  combo.push_back(plane_ix, cyl_ix);
+	  
+  // 	break;
+  //     else
+  // 	{
+  // 	  plane = false;
+  // 	  cyl = false;
+  // 	  dir.clear();
+  // 	  adjacent_ix.clear();
+  // 	  plane_ix = cyl_ix = -1;
+  // 	}
+  //   }
+
+  if (combo.size() == 0)
+    return false;
+
+  double fac = 1.1;
+  int max_num = 0;
+  int ix = -1;
+  double min_ang = M_PI;
+  for (size_t ki=0; ki<combo.size(); ++ki)
+    {
+      if (num_common[ki].first+num_common[ki].second > max_num /*&& angle[ki] < fac*min_ang*/)
+	{
+	  max_num = num_common[ki].first+num_common[ki].second;
+	  min_ang = angle[ki];
+	  ix = (int)ki;
+	}
+    }
+
+  if (ix < 0)
+    return false;
+  
+  plane_ix = (int)combo[ix].first;
+  cyl_ix = (int)combo[ix].second;
+  pos = adj[cyl_ix].first->location();
+  double radius = adj[cyl_ix].first->radius(0.0, 0.0);
+
+  // if (plane && cyl)
+  //   {
+  //     // Compute axis
+  //     for (size_t ki=1; ki<dir.size(); ++ki)
+  // 	if (dir[0]*dir[ki] < 0.0)
+  // 	  dir[ki] *= -1;
+
+      // axis = Point(0.0, 0.0, 0.0);
+      // double frac = 1.0/(double)dir.size();
+      // for (size_t ki=0; ki<dir.size(); ++ki)
+      // 	axis += frac*dir[ki];
+      outer = true;
+      axis = adj[plane_ix].first->direction();
+      Point axis2 = adj[cyl_ix].first->direction();
+
+#ifdef DEBUG_TORUSCONTEXT
+      std::ofstream of("adj_elem.g2");
+      for (size_t ki=0; ki<adjacent_ix.size(); ++ki)
+	{
+	  adj[adjacent_ix[ki]].first->writeStandardHeader(of);
+	  adj[adjacent_ix[ki]].first->write(of);
+	  adj[adjacent_ix[ki]].second->writeRegionInfo(of);
+	}
+#endif
+      
+      Cx = adj[cyl_ix].first->direction2();
+      Point Cy = axis.cross(Cx);
+      Cx = Cy.cross(axis);  // Project to plane
+      
+#ifdef DEBUG_TORUSCONTEXT
+      // Check accuracy of alternative plane
+      Point loc = adj[cyl_ix].first->location();
+      Point mid(0.0, 0.0, 0.0);
+      double wgt = 1.0/(double)(adj[plane_ix].second->numPoints());
+      for (auto it=adj[plane_ix].second->pointsBegin();
+	   it!=adj[plane_ix].second->pointsEnd(); ++it)
+	{
+	  Vector3D pos0 = (*it)->getPoint();
+	  Point xpos(pos0[0], pos0[1], pos0[2]);
+	  Point vec = xpos - loc;
+	  Point pos2 = loc + (vec*axis2)*axis2;
+	  mid += wgt*pos2;
+	}
+      shared_ptr<Plane> plane(new Plane(mid, axis2));
+      vector<RevEngPoint*> inptp, outptp;
+      vector<pair<double, double> > dist_angp;
+      double maxdp, avdp;
+      int num_inp, num2_inp;
+      vector<double> parvalsp;
+      RevEngUtils::distToSurf(adj[plane_ix].second->pointsBegin(),
+			      adj[plane_ix].second->pointsEnd(),
+			      plane, tol, maxdp, avdp, num_inp, num2_inp,
+			      inptp, outptp, parvalsp, dist_angp, -1.0);
+
+      std::ofstream ofd1("in_out_alt_plane.g2");
+      ofd1 << "400 1 0 4 155 50 50 255" << std::endl;
+      ofd1 << inptp.size() << std::endl;
+      for (size_t kr=0; kr<inptp.size(); ++kr)
+	ofd1 << inptp[kr]->getPoint() << std::endl;
+      ofd1 << "400 1 0 4 50 155 50 255" << std::endl;
+      ofd1 << outptp.size() << std::endl;
+      for (size_t kr=0; kr<outptp.size(); ++kr)
+	ofd1 << outptp[kr]->getPoint() << std::endl;
+      
+      // Check accuracy of alternative cylinder
+      vector<pair<vector<RevEngPoint*>::iterator,
+		  vector<RevEngPoint*>::iterator> > points;
+      points.push_back(std::make_pair(adj[cyl_ix].second->pointsBegin(),
+				      adj[cyl_ix].second->pointsEnd()));
+      BoundingBox bb = adj[cyl_ix].second->getBbox();
+      Point xpos;
+      double rad;
+      Point low = bb.low();
+      Point high = bb.high();
+      RevEngUtils::computeCylPosRadius(points, low, high, axis, Cx, Cy,
+				       xpos, rad);
+      shared_ptr<Cylinder> cyl(new Cylinder(rad, xpos, axis, Cy));
+      vector<RevEngPoint*> inpt, outpt;
+      vector<pair<double, double> > dist_ang;
+      double maxd, avd;
+      int num_in, num2_in;
+      vector<double> parvals;
+      RevEngUtils::distToSurf(points[0].first, points[0].second,
+			      cyl, tol, maxd, avd, num_in, num2_in,
+			      inpt, outpt, parvals, dist_ang, -1.0);
+
+      std::ofstream ofd2("in_out_alt_cyl.g2");
+      ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+      ofd2 << inpt.size() << std::endl;
+      for (size_t kr=0; kr<inpt.size(); ++kr)
+	ofd2 << inpt[kr]->getPoint() << std::endl;
+      ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+      ofd2 << outpt.size() << std::endl;
+      for (size_t kr=0; kr<outpt.size(); ++kr)
+	ofd2 << outpt[kr]->getPoint() << std::endl;
+#endif
+      
+      // Bound cylinder
+      int num_pt = adj[cyl_ix].second->numPoints();
+      double dist, ang;
+      int ka;
+      int num_in_cyl = 0;
+      for (ka=0; ka<num_pt; ++ka)
+	{
+	  Vector2D uv = adj[cyl_ix].second->getPoint(ka)->getPar();
+	  adj[cyl_ix].second->getPoint(ka)->getSurfaceDist(dist, ang);
+	  if (ang <= angtol)
+	    {
+	      num_in_cyl++;
+	      dom[0] = dom[1] = uv[0];
+	      dom[2] = dom[3] = uv[1];
+	      break;
+	    }
+	}
+      for (; ka<num_pt; ++ka)
+	{
+	  Vector2D uv = adj[cyl_ix].second->getPoint(ka)->getPar();
+	  adj[cyl_ix].second->getPoint(ka)->getSurfaceDist(dist, ang);
+	  if (ang > angtol)
+	    continue;
+	  num_in_cyl++;
+	  dom[0] = std::min(dom[0], uv[0]);
+	  dom[1] = std::max(dom[1], uv[0]);
+	  dom[2] = std::min(dom[2], uv[1]);
+	  dom[3] = std::max(dom[3], uv[1]);
+	}
+      if (dom[1] - dom[2] > 2*M_PI)
+	{
+	  dom[0] = 0.0;
+	  dom[1] = 2*M_PI;
+	}
+
+      if (num_in_cyl < 10)
+	return false;
+      
+#ifdef DEBUG_TORUSCONTEXT
+      shared_ptr<ElementarySurface> elem2(adj[cyl_ix].first->clone());
+      elem2->setParameterBounds(dom[0], dom[2], dom[1], dom[3]);
+      std::ofstream ofcyl("bd_cyl.g2");
+      elem2->writeStandardHeader(ofcyl);
+      elem2->write(ofcyl);
+#endif
+
+      // Compute distance from cylinder to plane
+      Point pos1 = adj[cyl_ix].first->point(0.5*(dom[0]+dom[1]), dom[2]);
+      Point pos2 = adj[cyl_ix].first->point(0.5*(dom[0]+dom[1]), dom[3]);
+      double upar1, upar2, upar3, vpar1, vpar2, vpar3, dist1, dist2, dist3;
+      Point close1, close2, close3;
+      double eps = 1.0e-6;
+      adj[plane_ix].first->closestPoint(pos1, upar1, vpar1, close1, dist1, eps);
+      adj[plane_ix].first->closestPoint(pos2, upar2, vpar2, close2, dist2, eps);
+      Point pos3 = (dist1 < dist2) ? pos + dom[2]*axis2 : pos + dom[3]*axis2;
+      adj[plane_ix].first->closestPoint(pos3, upar3, vpar3, close3, dist3, eps);
+
+      if ((close1-pos1)*axis < 0.0)
+	axis *= -1;
+      
+      if ((close1-pos1)*(close2-pos2) < 0.0)
+	{
+	  analyse_rotated = true;
+
+	  // Check axis direction
+	  int num1 = 0, num2 = 0;
+	  for (size_t ki=0; ki<group_points_.size(); ++ki)
+	    {
+	      Vector3D xyz = group_points_[ki]->getPoint();
+	      Point gpt(xyz[0], xyz[1], xyz[2]);
+	      if ((gpt-pos)*axis < 0.0)
+		num1++;
+	      else if ((gpt-pos)*axis > 0.0)
+		num2++;
+	    }
+	  if (num2 < num1)
+	    axis *= -1;
+	}
+      
+      R2 = std::min(dist1, dist2);
+      R1 = radius - R2;
+      pos = close3;
+
+      // Check if the torus in inwards or outwards
+      double cdist;
+      //RevEngPoint *closest_planar =
+      (void)adj[plane_ix].second->closestPoint(pos, cdist);
+      if (cdist > 0.99*radius)
+	{
+	  outer = false;
+	  R1 = radius + R2;
+	}
+
+#ifdef DEBUG_TORUSCONTEXT
+      // Bound plane
+      double dom2[4];
+      int num_pt2 = adj[plane_ix].second->numPoints();
+      Vector2D uv2 = adj[plane_ix].second->getPoint(0)->getPar();
+      dom2[0] = dom2[1] = uv2[0];
+      dom2[2] = dom2[3] = uv2[1];
+
+      for (int ka=0; ka<num_pt2; ++ka)
+	{
+	  Vector2D uv2 = adj[plane_ix].second->getPoint(ka)->getPar();
+	  dom2[0] = std::min(dom2[0], uv2[0]);
+	  dom2[1] = std::max(dom2[1], uv2[0]);
+	  dom2[2] = std::min(dom2[2], uv2[1]);
+	  dom2[3] = std::max(dom2[3], uv2[1]);
+	}
+      shared_ptr<ElementarySurface> elem3(adj[plane_ix].first->clone());
+      elem3->setParameterBounds(dom2[0], dom2[2], dom2[1], dom2[3]);
+      std::ofstream ofpl("bd_plane.g2");
+      elem3->writeStandardHeader(ofpl);
+      elem3->write(ofpl);
+#endif
+      
+      return true;
+  //   }
+  
+  // return false;  // Requested configuration not found   
+}
+
+//===========================================================================
+RevEngPoint* RevEngRegion::closestPoint(const Point& pos, double& dist)
+//===========================================================================
+{
+  dist = std::numeric_limits<double>::max();
+  int ix = -1;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector3D xyz = group_points_[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      double curr_dist = pos.dist(pt);
+      if (curr_dist < dist)
+	{
+	  dist = curr_dist;
+	  ix = (int)ki;
+	}
+    }
+
+  if (ix >= 0)
+    return group_points_[ix];
+  else
+    return 0;
+}
+
+//===========================================================================
+RevEngPoint* RevEngRegion::closestParPoint(const Point& parpt, double& dist)
+//===========================================================================
+{
+  if (!hasSurface())
+    return 0;
+  
+  dist = std::numeric_limits<double>::max();
+  int ix = -1;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector2D uv = group_points_[ki]->getPar();
+      Point pt(uv[0], uv[1]);
+      double curr_dist = parpt.dist(pt);
+      if (curr_dist < dist)
+	{
+	  dist = curr_dist;
+	  ix = (int)ki;
+	}
+    }
+
+  if (ix >= 0)
+    return group_points_[ix];
+  else
+    return 0;
+}
+
+//===========================================================================
+bool RevEngRegion::extractTorus(Point mainaxis[3], double tol, int min_pt,
+				int min_pt_reg, double angtol,
+				int prefer_elementary,
+				vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				vector<HedgeSurface*>& prevsfs,
+				vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  bool found = false;
+  int min_nmb = 20;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+  
+  // std::ofstream of("curr_region.g2");
+  // writeRegionInfo(of);
+
+  vector<Point> adj_axis;
+  axisFromAdjacent(angtol, adj_axis);
+  size_t num_adj = adj_axis.size();
+  for (int ka=0; ka<3; ++ka)
+    {
+      size_t kj;
+      for (kj=0; kj<num_adj; ++kj)
+	{
+	  double ang = mainaxis[ka].angle(adj_axis[kj]);
+	  ang = std::min(ang, M_PI-ang);
+	  if (ang <= angtol)
+	    break;
+	}
+      if (kj == num_adj)
+	adj_axis.push_back(mainaxis[ka]);
+    }
+  shared_ptr<Torus> tor1 = computeTorus(group_points_, adj_axis, tol,
+					angtol);
+  if (!tor1.get())
+    return false;
+  if (tor1->getMajorRadius() <= tor1->getMinorRadius())
+    return false;
+  
+  // Check accuracy
+  double maxd1, avd1, maxd2=100*tol, avd2=100*tol;
+  int num1, num1_2, num2=0, num2_2=0;
+  //shared_ptr<ParamSurface> surf = cyl;
+  vector<RevEngPoint*> in1, out1,in2, out2;
+  vector<pair<double, double> > dist_ang1;
+  vector<pair<double, double> > dist_ang2;
+  vector<double> parvals1;
+  vector<double> parvals2;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  tor1, tol, maxd1, avd1, num1, num1_2, in1, out1,
+			  parvals1, dist_ang1, angtol);
+  int sf_flag1 = defineSfFlag(0, tol, num1, num1_2, avd1, false);
+  
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofd("in_out_tor.g2");
+  ofd << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd << in1.size() << std::endl;
+  for (size_t kr=0; kr<in1.size(); ++kr)
+    ofd << in1[kr]->getPoint() << std::endl;
+  ofd << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd << out1.size() << std::endl;
+  for (size_t kr=0; kr<out1.size(); ++kr)
+    ofd << out1[kr]->getPoint() << std::endl;
+  // ofd << "400 1 0 4 155 50 50 255" << std::endl;
+  // ofd << in2.size() << std::endl;
+  // for (size_t kr=0; kr<in2.size(); ++kr)
+  //   ofd << in2[kr]->getPoint() << std::endl;
+  // ofd << "400 1 0 4 50 155 50 255" << std::endl;
+  // ofd << out2.size() << std::endl;
+  // for (size_t kr=0; kr<out2.size(); ++kr)
+  //   ofd << out2[kr]->getPoint() << std::endl;
+#endif
+
+  shared_ptr<Torus> tor2;
+    if (sf_flag1 > ACCURACY_OK)
+    {
+      Point pos = tor1->location();
+      Point axis = tor1->direction();
+      Point Cx = tor1->direction2();
+      tor2 = analyseRotatedTorus(pos, Cx, axis, tol, angtol);
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      tor2, tol, maxd2, avd2, num2, num2_2, in2, out2,
+			      parvals2, dist_ang2, angtol);
+    }
+
+  size_t insize = std::max(in1.size(), in2.size());
+  if (insize > group_points_.size()/2 && (int)insize > min_nmb)
+    {
+      shared_ptr<Torus> tor_in2;
+      shared_ptr<Torus> tor_in1 =
+	computeTorus(in1.size() > in2.size() ? in1 : in2, adj_axis,
+		     tol, angtol);
+if (tor_in1.get())
+{
+      vector<RevEngPoint*> inpt_in1, outpt_in1, inpt_in2, outpt_in2; 
+      vector<pair<double, double> > dist_ang_in1, dist_ang_in2;
+      double maxd_in1, avd_in1, maxd_in2, avd_in2;
+      int num1_in, num1_in2, num2_in, num2_in2;
+      vector<double> parvals_in1;
+      vector<double> parvals_in2;
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      tor_in1, tol, maxd_in1, avd_in1, num1_in, num1_in2,
+			      inpt_in1, outpt_in1, parvals_in1, dist_ang_in1,
+			      angtol);
+  
+      // RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+      // 			      tor_in2, tol, maxd_in2, avd_in2, num2_in, num2_in2,
+      // 			      inpt_in2, outpt_in2, parvals_in2,
+      // 			      dist_ang_in2, angtol);
+  
+#ifdef DEBUG_EXTRACT
+      std::ofstream ofd2("in_out_tor2.g2");
+      ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+      ofd2 << inpt_in1.size() << std::endl;
+      for (size_t kr=0; kr<inpt_in1.size(); ++kr)
+	ofd2 << inpt_in1[kr]->getPoint() << std::endl;
+      ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+      ofd2 << outpt_in1.size() << std::endl;
+      for (size_t kr=0; kr<outpt_in1.size(); ++kr)
+	ofd2 << outpt_in1[kr]->getPoint() << std::endl;
+      // ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+      // ofd2 << inpt_in2.size() << std::endl;
+      // for (size_t kr=0; kr<inpt_in2.size(); ++kr)
+      // 	ofd2 << inpt_in2[kr]->getPoint() << std::endl;
+      // ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+      // ofd2 << outpt_in2.size() << std::endl;
+      // for (size_t kr=0; kr<outpt_in2.size(); ++kr)
+      // 	ofd2 << outpt_in2[kr]->getPoint() << std::endl;
+#endif
+      
+      if (num1_in > num1 || avd_in1 < avd1)
+	{
+	  std::swap(tor1, tor_in1);
+	  std::swap(num1, num1_in);
+	  std::swap(num1_2, num1_in2);
+	  std::swap(avd1, avd_in1);
+	  std::swap(maxd1, maxd_in1);
+	  std::swap(parvals1, parvals_in1);
+	  std::swap(dist_ang1, dist_ang_in1);
+	  // std::cout << "Torus swap 1" << std::endl;
+	  // std::cout << group_points_.size() << " " << num1_in << " ";
+	  // std::cout << num1 << " " << avd_in1 << " " << avd1;
+	  // std::cout << " " << maxd_in1 << " " << maxd1 << std::endl;
+	}
+      // if (num2_in > num2 || avd_in2 < avd2)
+      // 	{
+      // 	  std::swap(tor2, tor_in2);
+      // 	  std::swap(num2, num2_in);
+      // 	  std::swap(num2_2, num2_in2);
+      // 	  std::swap(avd2, avd_in2);
+      // 	  std::swap(maxd2, maxd_in2);
+      // 	  std::swap(parvals2, parvals_in2);
+      // 	  std::swap(dist_ang2, dist_ang_in2);
+      // 	  // std::cout << "Torus swap 2" << std::endl;
+      // 	  // std::cout << group_points_.size() << " " << num2_in << " ";
+      // 	  // std::cout << num2 << " " << avd_in2 << " " << avd2;
+      // 	  // std::cout << " " << maxd_in2 << " " << maxd2 << std::endl;
+      // 	}
+    }
+    }
+  
+//int num = (int)group_points_.size();
+  double maxd_init, avd_init;
+  int num_init, num_init2;
+  getAccuracy(maxd_init, avd_init, num_init, num_init2);
+  int sf_flag2 = defineSfFlag(0, tol, num2, num2_2, avd2, false);
+  if (tor1->getMajorRadius() > tor1->getMinorRadius() &&
+      sf_flag1 < ACCURACY_POOR && num1 > num2)
+    {
+      bool OK = true;
+      if (associated_sf_.size() > 0)
+	{
+	  // Check with current approximating surface
+	  double acc_fac1 = 1.25;
+	  double acc_fac2 = 0.75;
+	  if (surfflag_ == ACCURACY_OK && sf_flag1 > surfflag_)
+	    OK = false;
+	  else if (prefer_elementary == ALWAYS_ELEM)
+	    OK = false;
+	  else if (prefer_elementary == PREFER_ELEM &&
+		   ((double)num1 < acc_fac1*num_init ||
+		    avd1 > acc_fac2*avd_init))
+	    OK = false;
+	  else if (prefer_elementary == BEST_ACCURACY &&
+		   (num1 < num_init || avd1 > avd_init))
+	    OK = false;
+	}
+
+      if (OK)
+	{
+	  found = true;
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals1[2*kh],parvals1[2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang1[kh].first, dist_ang1[kh].second);
+	    }
+	  setAccuracy(maxd1, avd1, num1, num1_2);      
+      
+#ifdef DEBUG
+	  std::cout << "Torus 1. N1: " << num << ", N2: " << num1 << ", max: " << maxd1 << ", av: " << avd1 << std::endl;
+#endif
+	  // associated_sf_.clear();
+	  // for (int ka=0; ka<divmod[0]->nmbEntities(); ++ka)
+	  // 	{
+	  //shared_ptr<ParamSurface> tor1_2 = divmod[0]->getSurface(ka);
+	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(tor1, this));
+	  for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+	    prevsfs.push_back(associated_sf_[kh]);
+	  setHedge(hedge.get());
+	  hedgesfs.push_back(hedge);
+	  setSurfaceFlag(sf_flag1);
+	}
+	// }
+	}
+  else if (tor2->getMajorRadius() > tor2->getMinorRadius() &&
+	   sf_flag2 < ACCURACY_POOR && num2 >= num1) 
+    {
+      bool OK = true;
+      if (associated_sf_.size() > 0)
+	{
+	  // Check with current approximating surface
+	  double acc_fac1 = 1.25;
+	  double acc_fac2 = 0.75;
+	  if (surfflag_ == ACCURACY_OK && sf_flag2 > surfflag_)
+	    OK = false;
+	  else if (prefer_elementary == ALWAYS_ELEM)
+	    OK = false;
+	  else if (prefer_elementary == PREFER_ELEM &&
+		   ((double)num2 < acc_fac1*num_init ||
+		    avd2 > acc_fac2*avd_init))
+	    OK = false;
+	  else if (prefer_elementary == BEST_ACCURACY &&
+		   (num2 < num_init || avd2 > avd_init))
+	    OK = false;
+	}
+
+      if (OK)
+	{
+	  found = true;
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals2[2*kh],parvals2[2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang2[kh].first, dist_ang2[kh].second);
+	    }
+	  setAccuracy(maxd2, avd2, num2, num2_2);
+      
+#ifdef DEBUG
+	  std::cout << "Torus 2. N1: " << num << ", N2: " << num2 << ", max: " << maxd2 << ", av: " << avd2 << std::endl;
+#endif
+	  // associated_sf_.clear();
+	  // for (int ka=0; ka<divmod[0]->nmbEntities(); ++ka)
+	  // 	{
+	  // 	  shared_ptr<ParamSurface> tor2_2 = divmod[0]->getSurface(ka);
+	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(tor2, this));
+	  for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+	    prevsfs.push_back(associated_sf_[kh]);
+	  setHedge(hedge.get());
+	  hedgesfs.push_back(hedge);
+	  setSurfaceFlag(sf_flag2);
+	  // }
+	}
+    }
+  if (!basesf_.get() ||
+      (num1 >= num_in_base_ && avd1 < avdist_base_))
+    setBaseSf(tor1, maxd1, avd1, num1, num1_2);
+  if (!basesf_.get() ||
+      (num2 >= num_in_base_ && avd2 < avdist_base_))
+    setBaseSf(tor2, maxd2, avd2, num2, num2_2);
+
+  return found;
+}
+ 
+//===========================================================================
+  shared_ptr<Torus> RevEngRegion::computeTorus(vector<RevEngPoint*>& points,
+					       vector<Point>& adj_axis,
+					       double tol, double angtol)
+//===========================================================================
+{
+#ifdef DEBUG_EXTRACT
+  std::ofstream of("torus_compute.g2");
+#endif
+  // Compute mean curvature and initial point in plane
+  double k2mean = 0.0;
+  double wgt = 1.0/(double)points.size();
+  for (size_t kr=0; kr<points.size(); ++kr)
+    {
+      double kmax = points[kr]->maxPrincipalCurvature();
+      k2mean += wgt*kmax;
+    }
+  double rd = 1.0/k2mean;
+  
+  vector<Point> centr(points.size());
+  Point mid(0.0, 0.0, 0.0);
+  for (size_t kr=0; kr<points.size(); ++kr)
+    {
+      double kmax = points[kr]->maxPrincipalCurvature();
+      k2mean += wgt*kmax;
+
+      Vector3D xyz = points[kr]->getPoint();
+      Point xyz2(xyz[0], xyz[1], xyz[2]);
+      Point norm = points[kr]->getLocFuncNormal();
+      centr[kr] = xyz2 + rd*norm;
+      mid += wgt*centr[kr];
+    }
+  
+#ifdef DEBUG_EXTRACT
+  of << "400 1 0 4 155 100 0 255" << std::endl;
+  of << points.size() << std::endl;
+  for (size_t kr=0; kr<points.size(); ++kr)
+    {
+      of << centr[kr] << std::endl;
+    }
+#endif
+
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  double len = low.dist(high);
+
+  ImplicitApprox impl;
+  impl.approxPoints(centr, 1);
+
+  double val;
+  Point grad;
+  impl.evaluate(mid, val, grad);
+  grad.normalize_checked();
+  
+  shared_ptr<Torus> dummy;
+  Point pos, normal;
+  bool found = impl.projectPoint(mid, grad, pos, normal);
+  if (!found)
+    return dummy;
+  double eps1 = 1.0e-8;
+  if (normal.length() < eps1)
+      return dummy;
+
+  for (size_t kr=0; kr<adj_axis.size(); ++kr)
+    {
+      double ang = adj_axis[kr].angle(normal);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < angtol)
+	{
+	  normal = adj_axis[kr];
+	  break;
+	}
+    }
+  
+#ifdef DEBUG_EXTRACT
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << "1" << std::endl;
+  of << pos << std::endl;
+  of << "410 1 0 4 255 0 0 255" << std::endl;
+  of << "1" << std::endl;
+  of << pos << " " << pos+0.5*len*normal << std::endl;
+#endif
+
+  // std::ofstream ofimpl("impl_plane.g2");
+  // impl->visualize(points, ofimpl);
+  
+  Vector3D xyz = points[0]->getPoint();
+  Point xyz2(xyz[0], xyz[1], xyz[2]);
+  Point Cx = centr[0] - xyz2;
+  Cx -= (Cx*normal)*normal;
+  Cx.normalize();
+  Point Cy = Cx.cross(normal);
+  
+  double rad;
+  Point pnt;
+  try {
+    RevEngUtils::computeCircPosRadius(centr, normal, Cx, Cy, pnt, rad);
+  }
+  catch (...)
+    {
+      shared_ptr<Torus> dummy;
+      return dummy;
+    }
+  pnt -= ((pnt - pos)*normal)*normal;
+  shared_ptr<Circle> circ(new Circle(rad, pnt, normal, Cx));
+#ifdef DEBUG_EXTRACT
+  circ->writeStandardHeader(of);
+  circ->write(of);
+#endif
+  
+//   vector<Point> rotated;
+//   vector<pair<vector<RevEngPoint*>::iterator,
+// 	      vector<RevEngPoint*>::iterator> > group;
+//   group.push_back(std::make_pair(points.begin(), points.end()));
+//   RevEngUtils::rotateToPlane(group, Cx, normal, pnt, rotated);
+// #ifdef DEBUG_EXTRACT
+//   std::ofstream of3("rotated_pts_tor.g2");
+//   of3 << "400 1 0 4 255 0 0 255" << std::endl;
+//   of3 << rotated.size() << std::endl;
+//   for (size_t kr=0; kr<rotated.size(); ++kr)
+//     of3 << rotated[kr] << std::endl;
+//   of3 << "410 1 0 4 0 0 255 255" << std::endl;
+//   of3 << "1" << std::endl;
+//   of3 << pnt-0.5*len*normal << " " << pnt+0.5*len*normal << std::endl;
+//   of3 << "410 1 0 4 0 255 0 255" << std::endl;
+//   of3 << "1" << std::endl;
+//   of3 << pnt-0.5*len*Cx << " " << pnt+0.5*len*Cx << std::endl;
+// #endif 
+//   Point cpos;
+//   double crad;
+//   RevEngUtils::computeCircPosRadius(rotated, Cy, Cx, normal, cpos, crad);
+//   shared_ptr<Circle> circ2(new Circle(crad, cpos, Cy, Cx));
+// #ifdef DEBUG_EXTRACT
+//   circ2->writeStandardHeader(of3);
+//   circ2->write(of3);
+// #endif
+//   shared_ptr<SplineCurve> spl;
+//   Point xpos;
+//   vector<double> param;
+//   try {
+//     curveApprox(rotated, tol, circ2, param, spl, xpos);
+//   }
+//   catch (...)
+//     {
+//     }
+// #ifdef DEBUG_EXTRACT
+// if (spl.get())
+//     {
+//       spl->writeStandardHeader(of3);
+//       spl->write(of3);
+//     }
+//  #endif
+  
+//   vector<Point> projected;
+//   double maxdp, avdp;
+//   RevEngUtils::projectToPlane(group, normal, pnt, projected, maxdp, avdp);
+
+// #ifdef DEBUG_EXTRACT
+//   std::ofstream ofp3("projected_pts_tor.g2");
+//   ofp3 << "400 1 0 4 255 0 0 255" << std::endl;
+//   ofp3 << projected.size() << std::endl;
+//   for (size_t kr=0; kr<projected.size(); ++kr)
+//     ofp3 << projected[kr] << std::endl;
+//   circ->writeStandardHeader(ofp3);
+//   circ->write(ofp3);
+// #endif
+  shared_ptr<Torus> tor1(new Torus(rad, fabs(rd), pnt, normal, Cy));
+
+#ifdef DEBUG_EXTRACT
+  tor1->writeStandardHeader(of);
+  tor1->write(of);
+#endif
+
+ //  Point cvec = cpos - pnt;
+//   double R1 = (cvec - (cvec*normal)*normal).length();
+//   double R2 = (cvec*normal)*normal.length();
+//   shared_ptr<Torus> tor2(new Torus(R1, crad, pnt+R2*normal, normal, Cy));
+// #ifdef DEBUG_EXTRACT
+//   tor2->writeStandardHeader(of);
+//   tor2->write(of);
+//   //std::cout << "Torus small radius: " << fabs(rd) << ", " << crad << std::endl;
+// #endif
+  
+//   torus2 = tor2;
+  return tor1;
+}
+
+//===========================================================================
+bool RevEngRegion::tryOtherSurf(int prefer_elementary, bool replace)
+//===========================================================================
+{
+  if (associated_sf_.size() > 0 && (!replace))
+    return false;
+  
+  if (prefer_elementary == BEST_ACCURACY ||
+      associated_sf_.size() == 0)
+    return true;
+
+  if (prefer_elementary != BEST_ACCURACY && surfflag_ == ACCURACY_OK)
+    return false;
+
+  if (prefer_elementary != BEST_ACCURACY && surf_adaption_ > INITIAL)
+    return false;
+
+  if (surfflag_ == ACCURACY_POOR)
+    return true;
+
+  if (prefer_elementary == ALWAYS_ELEM)
+    {
+      int sfcode;
+      ClassType type = associated_sf_[0]->instanceType(sfcode);
+      double fac = 5.0;
+      if ((type == Class_Plane || type == Class_Sphere) && MAH_ > fac*MAK_)
+	return true;
+      else
+	return false;
+    }
+  
+  // Check if the current accuracy is sufficient
+  int num = (int)group_points_.size();
+  double maxd_init, avd_init;
+  int num_init, num_init2;
+  getAccuracy(maxd_init, avd_init, num_init, num_init2);
+  if ((double)num_init > 0.75*num)
+    return false;
+  else
+    return true;
+  
+}
+
+
+//===========================================================================
+bool RevEngRegion::extractFreeform(double tol, int min_pt, int min_pt_reg,
+				   double angtol, int prefer_elementary,
+				   vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				   vector<HedgeSurface*>& prevsfs,
+				   vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  // std::ofstream of("curr_region.g2");
+  // writeRegionInfo(of);
+  
+  // std::ofstream of2("curr_normals.g2");
+  // writeUnitSphereInfo(of2);
+
+  bool found = false;
+  int min_nmb = 20;
+  //double eps = 1.0e-6;
+  if ((int)group_points_.size() < min_nmb)
+    return false;
+
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*> > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+  
+  shared_ptr<SplineSurface> spl = computeFreeform(group_points_, tol);
+  if (!spl.get())
+    return false;
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofs("spl.g2");
+  spl->writeStandardHeader(ofs);
+  spl->write(ofs);
+#endif
+  
+  // Check accuracy
+  double maxd, avd; 
+  int num2, num2_2; 
+  vector<RevEngPoint*> inpt, outpt;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+  			  spl, tol, maxd, avd, num2, num2_2, inpt, outpt,
+			  parvals, dist_ang, angtol);
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofd("in_out_spl.g2");
+  ofd << "400 1 0 4 155 50 50 255" << std::endl;
+  ofd << inpt.size() << std::endl;
+  for (size_t kr=0; kr<inpt.size(); ++kr)
+    ofd << inpt[kr]->getPoint() << std::endl;
+  ofd << "400 1 0 4 50 155 50 255" << std::endl;
+  ofd << outpt.size() << std::endl;
+  for (size_t kr=0; kr<outpt.size(); ++kr)
+    ofd << outpt[kr]->getPoint() << std::endl;
+#endif
+
+  int num = (int)group_points_.size();
+  double maxd_init, avd_init;
+  int num_init, num_init2;
+  getAccuracy(maxd_init, avd_init, num_init, num_init2);
+  if (associated_sf_.size() > 0 ||  (num2 > min_pt && num2 > num/2))
+    {
+      extractOutPoints(dist_ang, tol, angtol, out_groups);
+      if (out_groups.size() > 0)
+	{
+	  // Some points has been removed from the group. Redo surface
+	  // generation
+	  spl = computeFreeform(group_points_, tol);
+	  if (!spl.get())
+	    return false;
+#ifdef DEBUG_EXTRACT
+	  std::ofstream ofs2("spl2.g2");
+	  spl->writeStandardHeader(ofs2);
+	  spl->write(ofs2);
+#endif
+	  
+	  num = (int)group_points_.size();
+	  dist_ang.clear();
+	  parvals.clear();
+	  inpt.clear();
+	  outpt.clear();
+	  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+				  spl, tol, maxd, avd, num2, num2_2, inpt, outpt,
+				  parvals, dist_ang, angtol);
+#ifdef DEBUG_EXTRACT
+	  std::ofstream ofd2("in_out_spl2.g2");
+	  ofd2 << "400 1 0 4 155 50 50 255" << std::endl;
+	  ofd2 << inpt.size() << std::endl;
+	  for (size_t kr=0; kr<inpt.size(); ++kr)
+	    ofd2 << inpt[kr]->getPoint() << std::endl;
+	  ofd2 << "400 1 0 4 50 155 50 255" << std::endl;
+	  ofd2 << outpt.size() << std::endl;
+	  for (size_t kr=0; kr<outpt.size(); ++kr)
+	    ofd2 << outpt[kr]->getPoint() << std::endl;
+#endif
+	}
+      int stop_break_out = 1;
+    }
+
+  int sf_flag = defineSfFlag(0, tol, num2, num2_2, avd, false);
+  if (sf_flag < ACCURACY_POOR)
+    {
+     bool OK = true;
+      if (associated_sf_.size() > 0)
+	{
+	  // Check with current approximating surface
+	  double acc_fac1 = 1.25;
+	  double acc_fac2 = 0.75;
+	  if (surfflag_ == ACCURACY_OK && sf_flag > surfflag_)
+	    OK = false;
+	  else if (prefer_elementary == ALWAYS_ELEM)
+	    OK = false;
+	  else if (prefer_elementary == PREFER_ELEM &&
+		   ((double)num2 < acc_fac1*num_init ||
+		    avd > acc_fac2*avd_init))
+	    OK = false;
+	  else if (prefer_elementary == BEST_ACCURACY &&
+		   (num2 < num_init || avd > avd_init))
+	    OK = false;
+	}
+
+      if (OK)
+	{
+	  found = true;
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+	    }
+	  setAccuracy(maxd, avd, num2, num2_2);
+#ifdef DEBUG
+	  std::cout << "Spline. N1: " << num << ", N2: " << num2 << ", max: " << maxd << ", av: " << avd << std::endl;
+#endif
+	  shared_ptr<HedgeSurface> hedge(new HedgeSurface(spl, this));
+	  for (size_t kh=0; kh<associated_sf_.size(); ++kh)
+	    prevsfs.push_back(associated_sf_[kh]);
+	  setHedge(hedge.get());
+	  hedgesfs.push_back(hedge);
+	  setSurfaceFlag(sf_flag);
+	}
+    }
+  int stop_break0 = 1;
+  return found;
+}
+
+
+//===========================================================================
+ void RevEngRegion::removeAndUpdatePoints(vector<RevEngPoint*>& points)
+//===========================================================================
+ {
+   for (size_t ki=0; ki<points.size(); ++ki)
+     {
+       points[ki]->unsetRegion();
+       points[ki]->unsetSurfInfo();
+       vector<RevEngPoint*>::iterator it = std::find(group_points_.begin(),
+						     group_points_.end(),
+						     points[ki]);
+       if (it != group_points_.end())
+	 {
+	   std::swap(*it, group_points_[group_points_.size()-1]);
+	   group_points_.pop_back();
+	     //group_points_.erase(it);
+	 }
+      }
+ }
+
+//===========================================================================
+ void RevEngRegion::extractOutOfEdge(shared_ptr<CurveOnSurface>& cv,
+				     vector<shared_ptr<CurveOnSurface> >& intcv,
+				     double radius, double tol, double angtol,
+				     vector<RevEngPoint*>& out_points)
+//===========================================================================
+ {
+   if (!hasSurface())
+     return; // Points are not parameterized
+   shared_ptr<ParamCurve> pcv = cv->parameterCurve();
+   if (!pcv.get())
+     return;  // Not possible to check configuration in parameter domain
+
+   Point dir, norm0, norm;
+   if (pcv->isLinear(dir, angtol))
+       norm0 = Point(-dir[1],dir[0]);
+
+   // Check orientation of trimming curve
+   Point midp(0.5*(domain_[0]+domain_[1]), 0.5*(domain_[2]+domain_[3]));
+   double tpar1, dist1;
+   Point pclose1;
+   pcv->closestPoint(midp, pcv->startparam(), pcv->endparam(), tpar1, pclose1, dist1);
+   Point vec = pclose1 - midp;
+   if (norm0.dimension() > 0)
+     norm = norm0;
+   else
+     {
+       vector<Point> der(2);
+       pcv->point(der, tpar1, 1);
+       norm = Point(-der[1][1], der[1][0]);
+     }
+   int sgn = 0;
+   double dist2 = std::numeric_limits<double>::max();
+   for (size_t ki=0; ki<intcv.size(); ++ki)
+     {
+       shared_ptr<ParamCurve> pcv2 = intcv[ki]->parameterCurve();
+       if (!pcv2.get())
+	 continue;
+       double tpar2, dist2_2;
+       Point pclose2;
+       pcv2->closestPoint(midp, pcv2->startparam(), pcv2->endparam(), tpar2, pclose2, dist2_2);
+       if (dist2_2 < dist2)
+	 {
+	   dist2 = dist2_2;
+	   if (dist2 < dist1)
+	     sgn = (norm*vec < 0.0) ? 1 : -1;
+	   else
+	     sgn = (norm*vec < 0.0) ? -1 : 1;
+	 }
+     }
+   
+   if (sgn == 0)
+     sgn = (norm*vec < 0.0) ? -1 : 1;
+
+   // Check with point distribution
+   double mdist, cdist;
+   RevEngPoint *mclose = closestParPoint(midp, mdist);
+   RevEngPoint *cclose = closestParPoint(pclose1, cdist);
+
+   vector<RevEngPoint*> remaining;
+   for (size_t ki=0; ki<group_points_.size(); ++ki)
+     {
+       Vector2D uv = group_points_[ki]->getPar();
+       Point par(uv[0], uv[1]);
+
+       // Check if the parameter point lies to the left of the parameter curve
+       double tpar, dist;
+       Point close;
+       pcv->closestPoint(par, pcv->startparam(), pcv->endparam(), tpar,
+			 close, dist);
+
+       if (norm0.dimension() > 0)
+	 norm = norm0;
+       else
+	 {
+	   vector<Point> der(2);
+	   pcv->point(der, tpar, 1);
+	   norm = Point(-der[1][1], der[1][0]);
+	 }
+
+       vec = sgn*(close - par);
+       double ang = norm.angle(vec);
+       ang = std::min(ang, M_PI-ang);
+       if (norm*vec < 0.0 && ang <= angtol)
+	 {
+	   Vector3D xyz = group_points_[ki]->getPoint();
+	   Point pos(xyz[0], xyz[1], xyz[2]);
+	   double dist3 = std::numeric_limits<double>::max();
+	   for (size_t kj=0; kj<intcv.size(); ++kj)
+	     {
+	       shared_ptr<ParamCurve> space = intcv[kj]->spaceCurve();
+	       if (!space.get())
+		 continue;
+	       double tpar3, dist3_2;
+	       Point close3;
+	       space->closestPoint(pos, space->startparam(), space->endparam(),
+				   tpar3, close3, dist3_2);
+	       dist3 = std::min(dist3, dist3_2);
+	     }
+
+	   if (fabs(dist3-radius) < tol)
+	     out_points.push_back(group_points_[ki]);
+	   else
+	     remaining.push_back(group_points_[ki]);
+	 }
+       else
+	 remaining.push_back(group_points_[ki]);
+     }
+
+   if (out_points.size() > 0 && remaining.size() > 0)
+     {
+       // If all points are outside, none will be extracted
+       std::swap(group_points_, remaining);
+       updateInfo(tol, angtol);
+       
+       shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+       bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		       surf->instanceType() == Class_Cone);
+       int sf_flag = defineSfFlag(0, tol, num_inside_, num_inside2_, avdist_, cyllike);
+       setSurfaceFlag(sf_flag);
+     }
+ }
+
+//===========================================================================
+ void RevEngRegion::extractOutOfEdge2(vector<shared_ptr<CurveOnSurface> >& intcv,
+				      double tol, double angtol,
+				     vector<RevEngPoint*>& out_points)
+//===========================================================================
+ {
+   if (!hasSurface())
+     return; // Points are not parameterized
+   if (intcv.size() == 0)
+     return;
+
+   // Check orientation of trimming curve
+   double midt = 0.5*(intcv[0]->startparam() + intcv[intcv.size()-1]->endparam());
+   BoundingBox pbox(3);
+   int ix = -1;
+   for (size_t ki=0; ki<intcv.size(); ++ki)
+     {
+       if (intcv[ki]->startparam() <= midt && intcv[ki]->endparam() >= midt)
+	 {
+	   ix = (int)ki;
+	   break;
+	 }
+     }
+   if (ix < 0)
+     return;
+
+   vector<Point> der(2);
+   intcv[ix]->point(der, midt, 1);
+   Point sfpar = intcv[ix]->faceParameter(midt);
+   Point sfnorm;
+   shared_ptr<ParamSurface> surf = intcv[ix]->underlyingSurface();
+   surf->normal(sfnorm, sfpar[0], sfpar[1]);
+   Point vec = der[1].cross(sfnorm);
+   vec.normalize();
+   double diag = bbox_.low().dist(bbox_.high());
+   double dfac = 0.1;
+   Point pt1 = der[0] + dfac*diag*vec;
+   Point pt2 = der[0] - dfac*diag*vec;
+   double mdist1, mdist2;
+   RevEngPoint *rpt1, *rpt2;
+   rpt1 = closestPoint(pt1, mdist1);
+   rpt2 = closestPoint(pt2, mdist2);
+   int sgn = (mdist1 <= mdist2) ? -1 : 1;
+
+   vector<RevEngPoint*> remaining;
+   for (size_t ki=0; ki<group_points_.size(); ++ki)
+     {
+       Vector3D xyz = group_points_[ki]->getPoint();
+       Point pos(xyz[0], xyz[1], xyz[2]);
+
+       // Check if the point lies to the left of the curve
+       double tpar;
+       Point close, norm;
+       double dist = std::numeric_limits<double>::max();
+       int ix2 = -1;
+       for (size_t ki=0; ki<intcv.size(); ++ki)
+	 {
+	   double tpar2, dist2;
+	   Point close2;
+	   intcv[ki]->closestPoint(pos, intcv[ki]->startparam(), intcv[ki]->endparam(), tpar2, close2, dist2);
+	   if (dist2 < dist)
+	     {
+	       tpar = tpar2;
+	       dist = dist2;
+	       close = close2;
+	       ix2 = (int)ki;
+	     }
+	 }
+
+       if (ix2 < 0)
+	 remaining.push_back(group_points_[ki]);
+       else
+	 {
+	   vector<Point> der1(2);
+	   intcv[ix2]->point(der1, tpar, 1);
+	   Point sfpar2 = intcv[ix2]->faceParameter(tpar);
+	   Point sfnorm2;
+	   surf->normal(sfnorm2, sfpar2[0], sfpar2[1]);
+	   Point vec2 = der[1].cross(sfnorm);
+	   vector<Point> der2(2);
+	   intcv[ix]->point(der2, tpar, 1);
+	   Point vec3 = sgn*(der2[0] - pos);
+	   double ang = der2[1].angle(vec3);
+	   ang = fabs(0.5*M_PI - ang);
+	   
+	   if (vec2*vec3 < 0.0 &&  ang <= angtol)
+	     out_points.push_back(group_points_[ki]);
+	   else
+	     remaining.push_back(group_points_[ki]);
+	 }
+     }
+
+   if (out_points.size() > 0 && remaining.size() > 0)
+     {
+       // If all points are outside, none will be extracted
+       std::swap(group_points_, remaining);
+       updateInfo(tol, angtol);
+       
+       shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+       bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		       surf->instanceType() == Class_Cone);
+       int sf_flag = defineSfFlag(0, tol, num_inside_, num_inside2_, avdist_, cyllike);
+       setSurfaceFlag(sf_flag);
+     }
+ }
+
+ //===========================================================================
+ void RevEngRegion::extractOutPoints(vector<pair<double, double> >& dist_ang,
+				     double tol, double angtol,
+				     vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+ {
+   vector<RevEngPoint*> move;
+   for (size_t kr=0; kr<dist_ang.size(); ++kr)
+     {
+       if (dist_ang[kr].first > tol &&
+	   (associated_sf_.size() == 0 ||
+	    group_points_[kr]->getSurfaceDist() > tol))
+	 move.push_back(group_points_[kr]);
+     }
+   extractSpesPoints(move, out_groups, true);
+   if (out_groups.size() > 0)
+     updateInfo(tol, angtol);
+ }
+
+//===========================================================================
+ void RevEngRegion::identifyAngPoints(vector<pair<double, double> >& dist_ang,
+				      double tol, double disttol, 
+				      vector<RevEngPoint*>& ang_points)
+//===========================================================================
+ {
+   for (size_t kr=0; kr<dist_ang.size(); ++kr)
+     {
+       if (dist_ang[kr].second > tol && dist_ang[kr].first > disttol)
+	 ang_points.push_back(group_points_[kr]);
+     }
+ }
+//===========================================================================
+ void RevEngRegion::identifyAngPoints(vector<pair<double, double> >& dist_ang,
+				      double tol, double dtol, double dtol2,
+				      vector<RevEngPoint*>& ang_points,
+				      vector<RevEngPoint*>& remaining)
+//===========================================================================
+ {
+   for (size_t kr=0; kr<dist_ang.size(); ++kr)
+     {
+       if ((dist_ang[kr].second > tol && dist_ang[kr].first > dtol) ||
+	   dist_ang[kr].first > dtol2)
+	 ang_points.push_back(group_points_[kr]);
+       else
+	 remaining.push_back(group_points_[kr]);
+     }
+ }
+
+//===========================================================================
+ void RevEngRegion::identifyDistPoints(vector<pair<double, double> >& dist_ang,
+				       double tol, double maxd, double avd,
+				       vector<RevEngPoint*>& dist_points)
+//===========================================================================
+ {
+   double tol2 = (avd > 0.9*tol) ? 3.0*tol : 2.0*tol;
+   for (size_t kr=0; kr<dist_ang.size(); ++kr)
+     {
+       if (dist_ang[kr].first > tol2)
+	 dist_points.push_back(group_points_[kr]);
+     }
+ }
+
+//===========================================================================
+bool RevEngRegion::isConnected()
+//===========================================================================
+{
+  if (group_points_.size() == 0)
+    return false;
+  vector<RevEngPoint*> sub_group;
+  group_points_[0]->fetchConnected(this, (int)group_points_.size(), sub_group);
+
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    group_points_[ki]->unsetVisited();
+#ifdef DEBUG_CHECK
+  if (sub_group.size() < group_points_.size())
+    {
+      vector<vector<RevEngPoint*> > conn_groups;
+      std::vector<RevEngPoint*> dummy;
+      connectedGroups(group_points_, conn_groups, false, dummy);
+      std::ofstream of("disconnect_groups.g2");
+      for (size_t ki=0; ki<conn_groups.size(); ++ki)
+	{
+	  of << "400 1 0 4 0 155 100 255" << std::endl;
+	  of << conn_groups[ki].size() << std::endl;
+	  for (size_t kj=0; kj<conn_groups[ki].size(); ++kj)
+	    of << conn_groups[ki][kj]->getPoint() << std::endl;
+	}
+    }
+#endif
+
+  return (group_points_.size() == sub_group.size());
+}
+
+//===========================================================================
+ void RevEngRegion::connectedGroups(vector<RevEngPoint*>& move,
+				    vector<vector<RevEngPoint*> >& conn_groups,
+				    bool outer, vector<RevEngPoint*>& inner)
+//===========================================================================
+ {
+   // Note: derived information in this group is not updated!!
+   shared_ptr<RevEngRegion> dummy_reg(new RevEngRegion(edge_class_type_));
+      
+   for (size_t kr=0; kr<move.size(); ++kr)
+     move[kr]->setRegion(dummy_reg.get());  // Preliminary
+
+   vector<vector<RevEngPoint*> > sep_move;
+   for (size_t kr=0; kr<move.size(); ++kr)
+     {
+       if (move[kr]->visited())
+	 continue;
+       vector<RevEngPoint*> curr_move;
+       move[kr]->fetchConnected(dummy_reg.get(), (int)move.size(), curr_move);
+       sep_move.push_back(curr_move);
+     }
+      
+   for (size_t kr=0; kr<move.size(); ++kr)
+     move[kr]->unsetVisited();
+      
+#ifdef DEBUG_EXTRACT
+   std::ofstream m1("move_group.g2");
+   std::ofstream m2("not_move_group.g2");
+#endif
+   for (size_t kr=0; kr<sep_move.size(); ++kr)
+     {
+       size_t kh;
+       for (kh=0; kh<sep_move[kr].size(); ++kh)
+	 {
+	   vector<ftSamplePoint*> next = sep_move[kr][kh]->getNeighbours();
+	   size_t kh1;
+	   for (kh1=0; kh1<next.size(); ++kh1)
+	     {
+	       RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next[kh1]);
+		  
+	       RevEngRegion *reg = pt->region();
+	       if (reg != this && reg != dummy_reg.get())
+		 break;
+	     }
+	   if (kh1 < next.size())
+	     break;
+	 }
+       if (kh < sep_move[kr].size() || (!outer))
+	 {
+	   conn_groups.push_back(sep_move[kr]);
+#ifdef DEBUG_EXTRACT
+	   m1 << "400 1 0 0" << std::endl;
+	   m1 << sep_move[kr].size() << std::endl;
+	   for (size_t kh1=0; kh1<sep_move[kr].size(); ++kh1)
+	     m1 << sep_move[kr][kh1]->getPoint() << std::endl;
+#endif
+	 }
+       else
+	 {
+#ifdef DEBUG_EXTRACT
+	   m2 << "400 1 0 0" << std::endl;
+	   m2 << sep_move[kr].size() << std::endl;
+	   for (size_t kh1=0; kh1<sep_move[kr].size(); ++kh1)
+	     m2 << sep_move[kr][kh1]->getPoint() << std::endl;
+#endif
+	   inner.insert(inner.end(), sep_move[kr].begin(), sep_move[kr].end());
+	 }
+     }
+   for (size_t kr=0; kr<move.size(); ++kr)
+     move[kr]->setRegion(this);
+ }
+
+//===========================================================================
+ void RevEngRegion::extractSpesPoints(vector<RevEngPoint*>& move,
+				      vector<vector<RevEngPoint*> >& out_groups,
+				      bool outer)
+//===========================================================================
+ {
+   // Identify connected groups
+   std::vector<RevEngPoint*> dummy;
+   connectedGroups(move, out_groups, outer, dummy);
+   
+   // Extract group of out points from current group
+   for (size_t ki=0; ki<out_groups.size(); ++ki)
+     for (size_t kj=0; kj<out_groups[ki].size(); ++kj)
+       {
+	 out_groups[ki][kj]->unsetRegion();
+	 out_groups[ki][kj]->addMove();
+	 vector<RevEngPoint*>::iterator it = std::find(group_points_.begin(),
+						 group_points_.end(),
+						 out_groups[ki][kj]);
+	 if (it != group_points_.end())
+	   {
+	     std::swap(*it, group_points_[group_points_.size()-1]);
+	     group_points_.pop_back();
+	     //group_points_.erase(it);
+	   }
+       }
+
+   int stop_break = 1;
+ }
+ 
+//===========================================================================
+void RevEngRegion::removePoints(vector<RevEngPoint*>& remove)
+//===========================================================================
+ {
+   // Identify other points
+   std::vector<RevEngPoint*>::iterator end = group_points_.end();
+   std::vector<RevEngPoint*>::iterator last = group_points_.end();
+   --last;
+   for (size_t ki=0; ki<remove.size(); ++ki)
+     {
+       auto it = std::find(group_points_.begin(), end, remove[ki]);
+       if (it != end)
+	 {
+	   std::swap((*it), (*last));
+	   --last;
+	   --end;
+	 }
+     }
+   group_points_.erase(end, group_points_.end());
+ }
+
+//===========================================================================
+ void RevEngRegion::removeOtherPoints(vector<RevEngPoint*>& keep,
+				      vector<HedgeSurface*>& prevsfs,
+				      vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+ {
+   // Identify other points
+   vector<RevEngPoint*> remove;
+   for (size_t ki=0; ki<group_points_.size(); ++ki)
+     {
+       auto it = std::find(keep.begin(), keep.end(), group_points_[ki]);
+       if (it == keep.end())
+	 remove.push_back(group_points_[ki]);
+     }
+
+   int num = (int)group_points_.size();
+   if (remove.size() > 0)
+     {
+       extractSpesPoints(remove, out_groups, false);
+       updateInfo();
+     }
+
+   if (hasSurface() && (int)group_points_.size() < std::max(20, num/10))
+     {
+       prevsfs.insert(prevsfs.end(), associated_sf_.begin(), associated_sf_.end());
+       clearSurface();
+     }
+
+   for (size_t ki=0; ki<out_groups.size(); ++ki)
+     for (size_t kj=0; kj<out_groups[ki].size(); ++kj)
+       out_groups[ki][kj]->unsetRegion();
+   
+   updateRegionAdjacency();
+   int stop_break = 1;
+ }
+ 
+//===========================================================================
+shared_ptr<CurveOnSurface>
+RevEngRegion::constParSfCv(shared_ptr<ParamSurface> surf, int dir,
+			   double par, int bd, double t1, double t2)
+//===========================================================================
+{
+  double eps = std::max(0.0001*(t2-t1), 1.0e-4);
+  Point pdir = (dir == 0) ? Point(0.0, 1.0) : Point(1.0, 0.0);
+  vector<shared_ptr<ParamCurve> > cvs1 = surf->constParamCurves(par, (dir==1));
+  if (!cvs1[0]->isBounded())
+    {
+      shared_ptr<ElementaryCurve> elemcv =
+	dynamic_pointer_cast<ElementaryCurve,ParamCurve>(cvs1[0]);
+      elemcv->setParamBounds(t1, t2);
+    }
+
+  if (cvs1[0]->startparam() < t1-eps || cvs1[0]->endparam() > t2+eps)
+    {
+      shared_ptr<ParamCurve> sub(cvs1[0]->subCurve(t1, t2));
+      cvs1[0] = sub;
+    }
+  
+  Point ppos = (dir == 0) ? Point(par, 0.0) : Point(0.0, par);
+  shared_ptr<ElementaryCurve> pcrv(new Line(ppos, pdir));
+  pcrv->setParamBounds(cvs1[0]->startparam(), cvs1[0]->endparam());
+#ifdef DEBUG_TRIM
+  Point sp1, sp2, ssp1, ssp2;
+  Point pp1, pp2;
+  cvs1[0]->point(sp1, cvs1[0]->startparam());
+  cvs1[0]->point(sp2, cvs1[0]->endparam());
+  pcrv->point(pp1, pcrv->startparam());
+  pcrv->point(pp2, pcrv->endparam());
+  surf->point(ssp1, pp1[0], pp1[1]);
+  surf->point(ssp2, pp2[0], pp2[1]);
+#endif
+  shared_ptr<CurveOnSurface> sfcv(new CurveOnSurface(surf, pcrv, cvs1[0], false,
+						     3, dir+1, par, bd, true));
+  return sfcv;
+ }
+//===========================================================================
+bool RevEngRegion::trimSurface(double tol)
+//===========================================================================
+{
+  if (!hasSurface())
+    return false;
+
+  if (trim_edgs_.size() == 0)
+    return false;   // Not ready for trimming
+#ifdef DEBUG_TRIM
+  std::ofstream of1("par_edgs.g2");
+  std::ofstream of1_2("space_edgs.g2");
+ for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      shared_ptr<CurveOnSurface> sfcv =
+	dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+      if (!sfcv.get())
+	continue;
+      shared_ptr<ParamCurve> pcurve = sfcv->parameterCurve();
+      if (pcurve.get())
+	SplineDebugUtils::writeSpaceParamCurve(pcurve, of1, 0.0);
+      shared_ptr<ParamCurve> scurve = sfcv->spaceCurve();
+      if (scurve.get())
+	{
+	  scurve->writeStandardHeader(of1_2);
+	  scurve->write(of1_2);
+	}
+    }
+#endif
+  int status = 0;
+  HedgeSurface *hedge = getSurface(0);
+  shared_ptr<ParamSurface> surf = hedge->surface();
+  RectDomain dom = surf->containingDomain();
+  double dom2[4];
+  dom2[0] = dom.umin();
+  dom2[1] = dom.umax();
+  dom2[2] = dom.vmin();
+  dom2[3] = dom.vmax();
+  bool closed_u = false, closed_v = false;
+  shared_ptr<ElementarySurface> elem =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+  if (elem.get())
+    elem->isClosed(closed_u, closed_v);
+  else
+    {
+      shared_ptr<SplineSurface> splsf =
+	dynamic_pointer_cast<SplineSurface,ParamSurface>(surf);
+      if (splsf.get())
+	{
+	  int md1 = GeometryTools::analyzePeriodicityDerivs(*splsf, 0, 0, tol);
+	  int md2 = GeometryTools::analyzePeriodicityDerivs(*splsf, 1, 0, tol);
+	  closed_u = (md1 >= 0);
+	  closed_v = (md2 >= 0);
+	}
+    }
+
+  //double diag = bbox_.low().dist(bbox_.high());
+  if (closed_u)
+    {
+      // Add seem along v-direction to the edge collection
+      double tdel = domain_[3] - domain_[2];
+      double t1 = domain_[2] - 0.1*tdel;
+      double t2 = domain_[3] + 0.1*tdel;
+      shared_ptr<CurveOnSurface> seemcv1 = constParSfCv(surf, 0, dom2[0], 0, t1, t2);
+      shared_ptr<CurveOnSurface> seemcv2 = constParSfCv(surf, 0, dom2[1], 1, t1, t2);
+      shared_ptr<ftEdge> edg1(new ftEdge(hedge, seemcv1, seemcv1->startparam(),
+					 seemcv1->endparam()));
+      shared_ptr<ftEdge> edg2(new ftEdge(hedge, seemcv2, seemcv2->startparam(),
+					 seemcv2->endparam()));
+      int stat = 0;
+      edg1->connectTwin(edg2.get(), stat);
+      addTrimEdge(edg1);
+      addTrimEdge(edg2);
+    }
+  if (closed_v)
+    {
+      // Add seem along u-direction to the edge collection
+      double tdel = domain_[1] - domain_[0];
+      double t1 = domain_[0] - 0.1*tdel;
+      double t2 = domain_[1] + 0.1*tdel;
+      shared_ptr<CurveOnSurface> seemcv1 = constParSfCv(surf, 1, dom2[2], 2, t1, t2);
+      shared_ptr<CurveOnSurface> seemcv2 = constParSfCv(surf, 1, dom2[3], 3, t1, t2);
+      shared_ptr<ftEdge> edg1(new ftEdge(hedge, seemcv1, seemcv1->startparam(),
+					 seemcv1->endparam()));
+      shared_ptr<ftEdge> edg2(new ftEdge(hedge, seemcv2, seemcv2->startparam(),
+					 seemcv2->endparam()));
+      int stat = 0;
+      edg1->connectTwin(edg2.get(), stat);
+      addTrimEdge(edg1);
+      addTrimEdge(edg2);
+    }
+  
+  bool do_bound = false;  // Necessary to trim only if not all curves are boundary curves
+  vector<int> adjusted(trim_edgs_.size(), 0);
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      shared_ptr<CurveOnSurface> sfcv =
+	dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+      if (!sfcv.get())
+	continue;
+      if (sfcv->isConstantCurve())
+	{
+	  adaptOneEdge(trim_edgs_[ki], dom2);
+	  bool same;
+	  int bd = sfcv->whichBoundary(tol, same);
+#ifdef DEBUG_TRIM
+	  bool same_orient = sfcv->sameOrientation();
+	  bool same_trace = sfcv->sameTrace(tol);
+	  bool same_cv = sfcv->sameCurve(tol);
+	  if ((!same_orient) || (!same_trace) || (!same_cv))
+	    std::cout << "Surface curve mismatch " << ki << " " << sfcv << " " << same_orient << " " << same_trace << " " << same_cv << std::endl;
+	    
+#endif
+	  if (bd < 0)
+	    do_bound = true;
+	}
+      else
+	{
+	  do_bound = true;
+	    
+	  double t1 = sfcv->startparam();
+	  double t2 = sfcv->endparam();
+
+	  ftEdgeBase *twin0 = trim_edgs_[ki]->twin();
+	  if (twin0)
+	    {
+	      ftEdge *twin = twin0->geomEdge();
+	      shared_ptr<CurveOnSurface> sfcv2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(twin->geomCurve());
+	      double t3 = sfcv2->startparam();
+	      double t4 = sfcv2->endparam();
+	      if (sfcv2->isConstantCurve() && (t3 > t1 || t4 < t2))
+		{
+		  int mod = 0;
+		  if (t3 > t1)
+		    mod += 1;
+		  if (t4 < t2)
+		    mod += 2;
+		  double tmin = std::max(t1, t3);
+		  double tmax = std::min(t2, t4);
+		  shared_ptr<CurveOnSurface> sub(sfcv->subCurve(tmin, tmax));
+		  shared_ptr<ftEdge> subedge(new ftEdge(sub, tmin, tmax));
+		  ftEdgeBase *twin = trim_edgs_[ki]->twin();
+		  trim_edgs_[ki]->disconnectTwin();
+		  subedge->connectTwin(twin, status);
+		  trim_edgs_[ki] = subedge;
+		  adjusted[ki] = mod;
+		}
+	      int stop_break1 = 1;
+	    }
+	}
+    }
+  
+  if (do_bound == false && trim_edgs_.size() != 4)
+    {
+      // Check if the parameter bound in the surface and in the existing trimming edges
+      // is constent
+      int stop_check = 1;
+    }
+  
+#ifdef DEBUG_TRIM
+  std::ofstream of2("par_edgs2.g2");
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      shared_ptr<CurveOnSurface> sfcv =
+	dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+      if (!sfcv.get())
+	continue;
+      shared_ptr<ParamCurve> pcurve = sfcv->parameterCurve();
+      if (!pcurve.get())
+	continue;
+      SplineDebugUtils::writeSpaceParamCurve(pcurve, of2, 0.0);
+    }
+#endif
+
+  if (do_bound)
+    {
+      bool OK = arrangeEdgeLoop(tol, adjusted);
+      if (!OK)
+	return false;
+      if (trim_edgs_.size() == 0)
+	return false;
+
+      // Make bounded surface.
+      // First collect trimming curves
+      vector<shared_ptr<CurveOnSurface> > trim_cvs(trim_edgs_.size());
+      // NB! Could be missing curves
+      bool missing_par_cvs = false;
+      for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+	{
+	  shared_ptr<CurveOnSurface> tmp_sfcv =
+	    dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+	  trim_cvs[ki] = shared_ptr<CurveOnSurface>(tmp_sfcv->clone());  // To avoid inconsistencies
+	  // if two edges share the same curve in geometry space
+	  if (!trim_cvs[ki].get())
+	    {
+	      missing_par_cvs = true;
+	      continue;  // Should not happen
+	    }
+	  if (!trim_cvs[ki]->hasParameterCurve())
+	    missing_par_cvs = true;
+	}
+
+      if (missing_par_cvs)
+	return false;  // Will fail
+  
+      double eps = 1.0e-9;
+      vector<vector<shared_ptr<CurveOnSurface> > > trim_loops;
+
+      // Check for separate and disconnected loops
+      shared_ptr<ParamCurve> pcv1 = trim_cvs[0]->parameterCurve();
+      Point startpt = pcv1->point(pcv1->startparam());
+      int first_ix = 0;
+      for (int ka=0; ka<(int)trim_cvs.size()-1; ++ka)
+	{
+	  int kb = (ka+1)%(int)trim_cvs.size();
+	  shared_ptr<ParamCurve> pcv2 = trim_cvs[kb]->parameterCurve();
+	  Point end = pcv1->point(pcv1->endparam());
+	  Point start = pcv2->point(pcv2->startparam());
+	  double next_dd = end.dist(start);
+	  double close_dd = end.dist(startpt);
+	  if (close_dd <= next_dd+eps && close_dd <= tol)
+	    {
+	      vector<shared_ptr<CurveOnSurface> > curr_cvs(trim_cvs.begin()+first_ix,
+							   (kb!=0) ? trim_cvs.begin()+kb :
+							   trim_cvs.end());
+	      trim_loops.push_back(curr_cvs);
+	      startpt = start;
+	      first_ix = (kb!=0) ? kb : (int)trim_cvs.size();
+	    }
+	  else if (next_dd > tol)
+	    {
+	      // Open segment. Dismiss
+	      startpt = start;
+	      first_ix = (kb!=0) ? kb : (int)trim_cvs.size();
+	    }
+	      
+	  // if (end.dist(start) > tol)
+	  //   {
+	  //     // Check for a closed loop
+	  //     if (end.dist(startpt) <= tol)
+	  // 	{
+	  // 	  vector<shared_ptr<CurveOnSurface> > curr_cvs(trim_cvs.begin()+first_ix,
+	  // 						       (kb!=0) ? trim_cvs.begin()+kb :
+	  // 						       trim_cvs.end());
+	  // 	  trim_loops.push_back(curr_cvs);
+	  // 	  startpt = start;
+	  // 	  first_ix = (kb!=0) ? kb : (int)trim_cvs.size();
+	  // 	}
+	  //     else
+	  // 	{
+	  // 	  MESSAGE("RevEngRegion::trimSurface(): Obsolete code. Why do we end here?");
+		  
+	  // 	}
+	  //   }
+	  pcv1 = pcv2;
+	}
+
+      if (first_ix < (int)trim_cvs.size())
+	{
+	  vector<shared_ptr<CurveOnSurface> > curr_cvs(trim_cvs.begin()+first_ix,
+						       trim_cvs.end());
+	  trim_loops.push_back(curr_cvs);
+	}
+      
+#ifdef DEBUG_TRIM
+      std::ofstream of3("par_edgs3.g2");
+      std::ofstream of4("space_edgs3.g2");
+      for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+	{
+	  shared_ptr<CurveOnSurface> sfcv =
+	    dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+	  if (!sfcv.get())
+	    continue;
+	  shared_ptr<ParamCurve> pcurve = sfcv->parameterCurve();
+	  if (!pcurve.get())
+	    continue;
+	  SplineDebugUtils::writeSpaceParamCurve(pcurve, of3, 0.0);
+	}
+      for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+	{
+	  shared_ptr<CurveOnSurface> sfcv =
+	    dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+	  if (!sfcv.get())
+	    continue;
+	  shared_ptr<ParamCurve> scurve = sfcv->spaceCurve();
+	  scurve->writeStandardHeader(of4);
+	  scurve->write(of4);
+	}
+      std::ofstream of3_2("par_edgs3_2.g2");
+      std::ofstream of4_2("space_edgs3_2.g2");
+      for (size_t ki=0; ki<trim_loops.size(); ++ki)
+	for (size_t kj=0; kj<trim_loops[ki].size(); ++kj)
+	  {
+	    shared_ptr<CurveOnSurface> sfcv = trim_loops[ki][kj];
+	    shared_ptr<ParamCurve> pcurve = sfcv->parameterCurve();
+	    if (pcurve.get())
+	      SplineDebugUtils::writeSpaceParamCurve(pcurve, of3_2, 0.0);
+	    shared_ptr<ParamCurve> scurve = sfcv->spaceCurve();
+	    scurve->writeStandardHeader(of4_2);
+	    scurve->write(of4_2);
+	  }
+	
+#endif
+      // Check orientation
+      vector<CurveLoop> loops;
+      if (trim_loops.size() == 1)
+	{
+	  loops.push_back(CurveLoop(trim_loops[0], tol));
+	  bool is_CCW = LoopUtils::paramIsCCW(trim_loops[0], tol, tol);
+	  if (!is_CCW)
+	    {
+	      loops[0].turnOrientation();
+	      // Must also turn edges
+	    }
+	}
+      else
+	{
+	  // Sort curves. Expects one outer loop and one or more inner loops
+	  vector<Point> pt_on_cv(trim_loops.size());
+	  for (size_t ki=0; ki<trim_loops.size(); ++ki)
+	    {
+	      shared_ptr<ParamCurve> pcv = trim_loops[ki][0]->parameterCurve();
+	      pt_on_cv[ki] = pcv->point(0.5*(pcv->startparam()+pcv->endparam()));
+	    }
+
+	  size_t ki;
+	  for (ki=0; ki<trim_loops.size(); ++ki)
+	    {
+	      // Check if the point from all other loops lies inside current
+	      vector<shared_ptr<ParamCurve> > par_cvs(trim_loops[ki].size());
+	      for (size_t kr=0; kr<trim_loops[ki].size(); ++kr)
+		{
+		  par_cvs[kr] = trim_loops[ki][kr]->parameterCurve();
+		}
+
+	      shared_ptr<CurveLoop> cvloop(new CurveLoop(par_cvs, tol, false));
+	      CurveBoundedDomain cvdom(cvloop);
+	      size_t kj;
+	      for (kj=0; kj<pt_on_cv.size(); ++kj)
+		{
+		  if (kj == ki)
+		    continue;
+		  Vector2D ppos(pt_on_cv[kj][0], pt_on_cv[kj][1]);
+		  bool inside = cvdom.isInDomain(ppos, tol);
+		  if (!inside)
+		    break;
+		}
+	      if (kj == pt_on_cv.size())
+		break; // Outer boundary found
+	    }
+	  if (ki < trim_loops.size())
+	    std::swap(trim_loops[0], trim_loops[ki]);
+	  
+	  loops.push_back(CurveLoop(trim_loops[0], tol));
+	  bool is_CCW = LoopUtils::paramIsCCW(trim_loops[0], tol, tol);
+	  if (!is_CCW)
+	    {
+	      loops[0].turnOrientation();
+	    }
+	  for (size_t kr=1; kr<trim_loops.size(); ++kr)
+	    {
+	      loops.push_back(CurveLoop(trim_loops[kr], tol));
+	      bool is_CCW = LoopUtils::paramIsCCW(trim_loops[kr], tol, tol);
+	      if (is_CCW)
+		{
+		  loops[kr].turnOrientation();
+		}
+	    }
+	}
+
+      shared_ptr<BoundedSurface> bdsurf(new BoundedSurface(surf, loops));
+#ifdef DEBUG_TRIM
+      int valid_state;
+      bool valid = bdsurf->isValid(valid_state);
+      std::cout << "BoundedSurf is valid? " << valid << " " << valid_state << std::endl;
+      std::ofstream of5("bounded_surf.g2");
+      bdsurf->writeStandardHeader(of5);
+      bdsurf->write(of5);
+#endif
+      associated_sf_[0]->replaceSurf(bdsurf);
+
+      // // Add existing trimming curves. Later
+      // for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+      // 	trim_edgs_[ki]->setFace(associated_sf_[0]);
+      // vector<shared_ptr<ftEdgeBase> > tmp_trim(trim_edgs_.begin(), trim_edgs_.end());
+      // shared_ptr<Loop> edge_loop(new Loop(associated_sf_[0], tmp_trim, tol));
+      // associated_sf_[0]->addOuterBoundaryLoop(edge_loop);
+      associated_sf_[0]->clearInitialEdges();
+      (void)associated_sf_[0]->createInitialEdges();
+#ifdef DEBUG_TRIM
+      for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+	{
+	  shared_ptr<CurveOnSurface> sfcv =
+	    dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+	  Point startpt1 = sfcv->ParamCurve::point(sfcv->startparam());
+	  Point startpt2 = trim_edgs_[ki]->point(trim_edgs_[ki]->tMin());
+	  int stop_breakc = 1;
+	}
+#endif
+    }  // End do_bound
+  
+  return true;
+}
+
+struct CloseCvInfo
+{
+  double par1_, par2_, dist_;
+  double par3_, par4_, dist2_;
+
+  CloseCvInfo()
+  {
+    par1_ = par2_ = par3_ = par4_ = 0.0;
+    dist_ = dist2_ = -1.0;
+  }
+  
+  CloseCvInfo(double par1, double par2, double dist)
+  {
+    par1_ = par1;
+    par2_ = par2;
+    dist_ = dist;
+    par3_ = par4_ = 0.0;
+    dist2_ = -1.0;
+  }
+  
+  CloseCvInfo(double par1, double par2, double dist, 
+	      double par3, double par4, double dist2)
+  {
+    par1_ = par1;
+    par2_ = par2;
+    dist_ = dist;
+    par3_ = par3;
+    par4_ = par4;
+    dist2_ = dist2;
+  }
+};
+
+CloseCvInfo getCloseInfo(double tol, shared_ptr<ParamCurve>& pcurve1, int adjusted1,
+			 shared_ptr<ParamCurve>& pcurve2, int adjusted2)
+{
+  Point close1, close2;
+  int status = 0;
+  double tmin1 = pcurve1->startparam();
+  double tmax1 = pcurve1->endparam();
+  double seed1 = 0.5*(tmin1 + tmax1);
+  Point pos1 = pcurve1->point(tmin1);
+  Point pos2 = pcurve1->point(tmax1);
+  double tmin2 = pcurve2->startparam();
+  double tmax2 = pcurve2->endparam();
+  double seed2 = 0.5*(tmin2 + tmax2);
+  Point pos3 = pcurve2->point(tmin2);
+  Point pos4 = pcurve2->point(tmax2);
+  double par1[9], par2[9];
+  par1[0] = par1[1] = tmin1;
+  par1[2] = par1[3] = tmax1;
+  par1[5] = tmin1;
+  par1[6] = tmax1;
+  par2[7] = tmin2;
+  par2[8] = tmax2;
+  par2[0] = par2[2] = tmin2;
+  par2[1] = par2[3] = tmax2;
+  par1[4] = par1[7] = par1[8] = 0.5*(tmin1+tmax1);
+  par2[4] = par2[5] = par2[6] = 0.5*(tmin2+tmax2);
+  double dist[9];
+  dist[0] = pos1.dist(pos3);
+  dist[1] = pos1.dist(pos4);
+  dist[2] = pos2.dist(pos3);
+  dist[3] = pos2.dist(pos4);
+  dist[4] = dist[5] = dist[6] = dist[7] = dist[8] = std::numeric_limits<double>::max();
+  if (adjusted1 < 3 && adjusted2 < 3)
+    ClosestPoint::closestPtCurves2D(pcurve1.get(), pcurve2.get(), tol,
+				    tmin1, tmax1, tmin2, tmax2, seed1,
+				    seed2, 1, false, par1[4], par2[4],
+				    dist[4], close1, close2, status);
+  if ((adjusted1 == 1 || adjusted1 == 3) && adjusted2 < 3)
+    pcurve2->closestPoint(pos1, tmin2, tmax2, par2[5], close2, dist[5]);
+  if ((adjusted2 == 1 || adjusted1 == 3) && adjusted2 < 3)
+    pcurve2->closestPoint(pos2, tmin2, tmax2, par2[6], close2, dist[6]);
+  if ((adjusted2 == 1 || adjusted2 == 3) && adjusted1 < 3)
+    pcurve1->closestPoint(pos3, tmin1, tmax1, par1[7], close1, dist[7]);
+  if ((adjusted2 == 2 || adjusted2 == 3) && adjusted1 < 3)
+    pcurve1->closestPoint(pos4, tmin1, tmax1, par1[8], close1, dist[8]);
+
+  double eps = std::min(0.1*tol, 1.0e-4);
+  double eps2 = 1.0e-9;
+  for (int ka=4; ka<9; ++ka)
+    {
+      if (par1[ka]-tmin1 < eps2)
+	par1[ka] = tmin1;
+      if (tmax1-par1[ka] < eps2)
+	par1[ka] = tmax1;
+      if (par2[ka]-tmin2 < eps2)
+	par2[ka] = tmin2;
+      if (tmax2-par2[ka] < eps2)
+	par2[ka] = tmax2;
+    }
+  
+  if (adjusted2 == 1 || adjusted2 == 3)
+    {
+      if (par1[4] - tmin1 > eps2 && par1[4] - tmin1 < eps)
+	dist[4] = std::numeric_limits<double>::max();
+      if (par1[7] - tmin1 > eps2 && par1[7] - tmin1 < eps)
+	dist[7] = std::numeric_limits<double>::max();
+      if (par1[8] - tmin1 > eps2 && par1[8] - tmin1 < eps)
+	dist[8] = std::numeric_limits<double>::max();
+    }
+  if (adjusted2 == 2 || adjusted2 == 3)
+    {
+      if (tmax1 - par1[4] > eps2 && tmax1 - par1[4] < eps)
+	dist[4] = std::numeric_limits<double>::max();
+      if (tmax1 - par1[7] > eps2 && tmax1 - par1[7] < eps)
+	dist[7] = std::numeric_limits<double>::max();
+      if (tmax1 - par1[8] > eps2 && tmax1 - par1[8] < eps)
+	dist[8] = std::numeric_limits<double>::max();
+    }
+
+  if (adjusted1 == 1 || adjusted1 == 3)
+    {
+      if (par2[4] - tmin2 > eps2 && par2[4] - tmin2 < eps)
+	dist[4] = std::numeric_limits<double>::max();
+      if (par2[5] - tmin2 > eps2 && par2[5] - tmin2 < eps)
+	dist[5] = std::numeric_limits<double>::max();
+      if (par2[6] - tmin2 > eps2 && par2[6] - tmin2 < eps)
+	dist[6] = std::numeric_limits<double>::max();
+    }
+  if (adjusted1 == 2 || adjusted1 == 3)
+    {
+      if (tmax2 - par2[4] > eps2 && tmax2 - par2[4] < eps)
+	dist[4] = std::numeric_limits<double>::max();
+      if (tmax2 - par2[5] > eps2 && tmax2 - par2[5] < eps)
+	dist[5] = std::numeric_limits<double>::max();
+      if (tmax2 - par2[6] > eps2 && tmax2 - par2[6] < eps)
+	dist[6] = std::numeric_limits<double>::max();
+    }
+
+
+  int ka, kb;
+  for (ka=0; ka<9; ++ka)
+    for (kb=ka+1; kb<9; ++kb)
+      {
+	// if ((kb < 4 && dist[kb] < dist[ka]) || (kb == 4 && dist[kb] < dist[ka]-tol))
+	if (dist[kb] < dist[ka])
+	  {
+	    std::swap(dist[ka], dist[kb]);
+	    std::swap(par1[ka], par1[kb]);
+	    std::swap(par2[ka], par2[kb]);
+	  }
+      }
+
+  if (fabs(par1[1] - par1[0]) < eps || fabs(par2[1]-par2[0]) < eps)
+    {
+      par1[1] = par1[2];
+      par2[1] = par2[2];
+      dist[1] = dist[2];
+    }
+  CloseCvInfo curr_info(par1[0], par2[0], dist[0], par1[1],
+			par2[1], dist[1]);
+  return curr_info;
+ }
+
+
+//===========================================================================
+bool RevEngRegion::arrangeEdgeLoop(double tol, vector<int>& adjusted)
+//===========================================================================
+{
+  vector<vector<CloseCvInfo> > info(trim_edgs_.size());
+  vector<shared_ptr<CurveOnSurface> > sfcv(trim_edgs_.size());
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      info[ki].resize(trim_edgs_.size());
+      sfcv[ki] =
+	dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+    }
+
+  // Collect distance info
+  double tmin1, tmax1; 
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      if (!sfcv[ki].get())
+	continue;
+      shared_ptr<ParamCurve> pcurve1 = sfcv[ki]->parameterCurve();
+      if (!pcurve1.get())
+	return false;
+      
+      tmin1 = pcurve1->startparam();
+      tmax1 = pcurve1->endparam();
+      Point pos1 = pcurve1->point(tmin1);
+      Point pos2 = pcurve1->point(tmax1);
+      double dist0 = pos1.dist(pos2);
+      info[ki][ki] = CloseCvInfo(tmin1, tmax1, dist0);
+      for (size_t kj=ki+1; kj<trim_edgs_.size(); ++kj)
+	{
+	  if (!sfcv[kj].get())
+	    continue;
+	  shared_ptr<ParamCurve> pcurve2 = sfcv[kj]->parameterCurve();
+	  if (!pcurve2.get())
+	    return false;
+
+	  CloseCvInfo curr_info = getCloseInfo(tol, pcurve1, adjusted[ki],
+					       pcurve2, adjusted[kj]);
+	  info[ki][kj] = info[kj][ki] = curr_info;
+ 	}
+    }
+
+  //#if 0
+  // Identify gaps
+  double tol2 = 10.0*tol;
+  vector<pair<int, int> > adj_gap;
+  for (size_t ki=0; ki<info.size(); ++ki)
+    {
+      double ix1 = -1, ix2 = -1;
+      for (size_t kj=0; kj<info[ki].size(); ++kj)
+	{
+	  // if (kj == ki)
+	  //   continue;
+	  double dd = info[ki][kj].dist_;
+	  if (dd <= tol2)
+	    {
+	      int num_small = 0;
+	      for (size_t kr=0; kr<info[kj].size(); ++kr)
+		if (info[kj][kr].dist_ < dd)
+		  num_small++;
+	      if (num_small < 2)
+		{
+		  if (ix1 < 0)
+		    ix1 = (int)kj;
+		  else if (ix2 < 0)
+		    ix2 = (int)kj;
+		}
+	    }
+	}
+      if (ix1 < 0 || (ix2 < 0 && ix1 != (int)ki))
+	adj_gap.push_back(std::make_pair((int)ki, ix1));
+    }
+
+  vector<double> limit_par;
+  if (adj_gap.size() > 0)
+    {
+      for (size_t ki=0; ki<adj_gap.size(); ++ki)
+	{
+	  // Fetch adjacent face
+	  int cv_ix = adj_gap[ki].first;
+	  ftEdgeBase* twin0 = trim_edgs_[cv_ix]->twin();
+	  if (!twin0)
+	    continue;
+	  ftEdge* twin = twin0->geomEdge();
+	  ftFaceBase *twin_face = twin->face();
+	  if (!twin_face)
+	    continue;
+	  HedgeSurface *adj_hedge = dynamic_cast<HedgeSurface*>(twin_face);
+	  if (!adj_hedge)
+	    continue;
+	  RevEngRegion* other_reg = adj_hedge->getRegion(0);
+	  vector<RevEngPoint*> edge_pts;
+	  if (other_reg == this)
+	    {
+	      // Fetch points at seem
+	      int dir;
+	      double parval;
+	      sfcv[cv_ix]->isConstantCurve(tol2, dir, parval);
+	      bool udir = (dir == 1);   // Should be sufficient
+	      vector<RevEngPoint*> seam_pts1, seam_pts2;
+	      extractPointsAtSeam(seam_pts1, seam_pts2, udir);
+	      int ix = 1 - dir;
+	      if (parval-domain_[2*ix] < domain_[2*ix+1]-parval)
+		edge_pts = seam_pts1;
+	      else
+		edge_pts = seam_pts2;
+	      int stop_b1 = 1;
+	    }
+	  else
+	    {
+	      vector<RevEngRegion*> others;
+	      others.push_back(other_reg);
+	      edge_pts = extractBdPoints(others);
+	      int stop_b2 = 1;
+	    }
+
+	  // Identify first and last point along edge
+	  RevEngPoint *first_pt=0, *last_pt=0;
+	  double t1, t2;
+	  RevEngUtils::identifyEndPoints(edge_pts, sfcv[cv_ix], first_pt, t1, last_pt, t2);
+#ifdef DEBUG_TRIM
+	  if (t2 > t1)
+	    {
+	      std::ofstream ofpt("edge_ends.g2");
+	      if (first_pt)
+		{
+		  ofpt << "400 1 0 4 100 100 55 255" << std::endl;
+		  ofpt << "1" << std::endl;
+		  ofpt << first_pt->getPar() << " 0.0" << std::endl;
+		}
+	      if (last_pt)
+		{
+		  ofpt << "400 1 0 4 100 100 55 255" << std::endl;
+		  ofpt << "1" << std::endl;
+		  ofpt << last_pt->getPar() << " 0.0" << std::endl;
+		}
+	    }
+#endif
+	  if (t2 < t1)
+	    std::swap(t1, t2);
+	  if (adj_gap[ki].second < 0)
+	    {
+	      limit_par.push_back(t1);
+	      limit_par.push_back(t2);
+	    }
+	  else
+	    {
+	      int ix2 = adj_gap[ki].second;
+	      double t3 = (cv_ix < ix2) ? info[cv_ix][ix2].par1_ : info[cv_ix][ix2].par2_;
+	      if (fabs(t3 - t1) < fabs(t3 - t2))
+		limit_par.push_back(t2);
+	      else limit_par.push_back(t1);
+	    }
+	  int stop_break0 = 1;
+	}
+
+      // Sort gaps
+      vector<pair<pair<shared_ptr<CurveOnSurface>,double>,
+		  pair<shared_ptr<CurveOnSurface>,double> > > gap_cv_bounds;
+      vector<bool> use_lower;
+      if (adj_gap.size() == 1 && limit_par.size() == 2)
+	{
+	  int cv_ix = adj_gap[0].first;
+	  gap_cv_bounds.push_back(make_pair(make_pair(sfcv[cv_ix],limit_par[0]),
+					    make_pair(sfcv[cv_ix],limit_par[1])));
+	  use_lower.push_back(true);  // Not used
+	}
+      else if (adj_gap.size() == 2 && limit_par.size() == 2)
+	{
+	  int cv_ix1 = adj_gap[0].first;
+	  int cv_ix2 = adj_gap[1].first;
+	  double t3;
+	  if (trim_edgs_[cv_ix1]->twin() == trim_edgs_[cv_ix2].get())
+	    {
+	      // Seam curve. Unify end parameters
+	      int ix2 = adj_gap[0].second;
+	      t3 = (cv_ix1 < ix2) ? info[cv_ix1][ix2].par1_ : info[cv_ix1][ix2].par2_;
+	      if (fabs(t3-limit_par[0]) < fabs(t3-limit_par[1]))
+		limit_par[0] = limit_par[1];
+	      else
+		limit_par[1] = limit_par[0];
+	    }
+	  gap_cv_bounds.push_back(make_pair(make_pair(sfcv[cv_ix1],limit_par[0]),
+					    make_pair(sfcv[cv_ix2],limit_par[1])));
+	  use_lower.push_back((limit_par[0]<t3));
+	}
+      else
+	MESSAGE("RevEngRegion::arrangeEdgeLoop(): Gap configuration not implemented");
+
+      HedgeSurface *hedge = getSurface(0);
+      shared_ptr<ParamSurface> surf = hedge->surface();
+      size_t num = trim_edgs_.size();
+      for (size_t ki=0; ki<gap_cv_bounds.size(); ++ki)
+	{
+	  shared_ptr<CurveOnSurface> sfcv1 = gap_cv_bounds[ki].first.first;
+	  shared_ptr<CurveOnSurface> sfcv2 = gap_cv_bounds[ki].second.first;
+	  int dir1, dir2;
+	  double parval1, parval2;
+	  sfcv1->isConstantCurve(tol, dir1, parval1);
+	  sfcv2->isConstantCurve(tol, dir2, parval2);
+
+	  shared_ptr<ParamCurve> pcv1 = sfcv1->parameterCurve();
+	  shared_ptr<ParamCurve> pcv2 = sfcv2->parameterCurve();
+	  Point ppos1 = pcv1->point(gap_cv_bounds[ki].first.second);
+	  Point ppos2 = pcv2->point(gap_cv_bounds[ki].second.second);
+	  if (dir1 >= 0 && dir1 == dir2)
+	    {
+	      // Set surface limit based on parameter domain of points
+	      int p_ix = 2 - dir1;
+	      // if (std::min(fabs(ppos1[p_ix]-domain_[2*p_ix]), fabs(ppos1[p_ix]-domain_[2*p_ix])) <
+	      // 	  std::min(fabs(ppos1[p_ix]-domain_[2*p_ix+1]), fabs(ppos1[p_ix]-domain_[2*p_ix+1])))
+	      if (use_lower[ki])
+		ppos1[p_ix] = ppos2[p_ix] = domain_[2*p_ix];
+	      else
+		ppos1[p_ix] = ppos2[p_ix] = domain_[2*p_ix+1];
+	    }
+	  shared_ptr<SplineCurve> gap_par(new SplineCurve(ppos1, ppos2));
+	  shared_ptr<CurveOnSurface> gap_sfcv(new CurveOnSurface(surf, gap_par, true));
+	  gap_sfcv->ensureSpaceCrvExistence(tol);
+	  shared_ptr<ftEdge> gap_edge(new ftEdge(hedge, gap_sfcv, gap_sfcv->startparam(),
+						 gap_sfcv->endparam()));
+	  sfcv.push_back(gap_sfcv);
+	  trim_edgs_.push_back(gap_edge);
+	  adjusted.push_back(true);
+	}
+
+      if (sfcv.size() > num)
+	{
+	  // Extend meeting information
+	  info.resize(sfcv.size());
+	  for (size_t ki=0; ki<sfcv.size(); ++ki)
+	    info[ki].resize(sfcv.size());
+
+	  for (size_t ki=num; ki<trim_edgs_.size(); ++ki)
+	    {
+	      if (!sfcv[ki].get())
+		continue;
+	      shared_ptr<ParamCurve> pcurve1 = sfcv[ki]->parameterCurve();
+	      if (!pcurve1.get())
+		continue;
+      
+	      tmin1 = pcurve1->startparam();
+	      tmax1 = pcurve1->endparam();
+	      Point pos1 = pcurve1->point(tmin1);
+	      Point pos2 = pcurve1->point(tmax1);
+	      double dist0 = pos1.dist(pos2);
+	      info[ki][ki] = CloseCvInfo(tmin1, tmax1, dist0);
+	      for (size_t kj=0; kj<trim_edgs_.size(); ++kj)
+		{
+		  if (kj >= num && kj <= ki)
+		    continue;
+		  if (!sfcv[kj].get())
+		    continue;
+		  shared_ptr<ParamCurve> pcurve2 = sfcv[kj]->parameterCurve();
+		  if (!pcurve2.get())
+		    continue;
+
+		  CloseCvInfo curr_info = (ki > kj) ?
+		    getCloseInfo(tol, pcurve2, adjusted[kj], pcurve1, adjusted[ki]) :
+		    getCloseInfo(tol, pcurve1, adjusted[ki], pcurve2, adjusted[kj]);;
+		  info[ki][kj] = info[kj][ki] = curr_info;
+		}
+	    }
+
+	}
+      int stop_break = 1;
+    }
+  //#endif  
+  // Check if any curves must be reduced, and record previous and next curve
+  double eps = 1.0e-9;
+  int status = 0;
+  vector<pair<int, int> > seq(trim_edgs_.size());
+  vector<pair<double, double> > param(trim_edgs_.size());
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      int ix1=-1, ix2=-1;
+      double td1 = std::numeric_limits<double>::max();
+      double td2 = std::numeric_limits<double>::max();
+      double td0 = info[ki][ki].dist_;
+      double t1, t2;
+      t1 = t2 = 2.0*fabs(std::max(sfcv[ki]->startparam(), sfcv[ki]->endparam()));
+      double delta = 0.001*fabs(info[ki][ki].par2_ - info[ki][ki].par1_);
+      for (size_t kj=0; kj<info[ki].size(); ++kj)
+	{
+	  if (ki == kj)
+	    continue;
+	  double dd = info[ki][kj].dist_;
+	  double tp1 = (ki < kj) ? info[ki][kj].par1_ : info[ki][kj].par2_;
+	  if (ix1 < 0 || dd < td1)
+	    {
+	      ix1 = (int)kj;
+	      td1 = dd;
+	      t1 = tp1;
+	    }
+	}
+
+      
+      for (size_t kj=0; kj<info[ki].size(); ++kj)
+	{
+	  if (ki == kj)
+	    continue;
+	  double dd = info[ki][kj].dist_;
+	  double dd2 = info[ki][kj].dist2_;
+	  double tp1 = (ki < kj) ? info[ki][kj].par1_ : info[ki][kj].par2_;
+	  double tp2 = (ki < kj) ? info[ki][kj].par3_ : info[ki][kj].par4_;
+	  //double td12 = std::max(td1, td2);
+	  if (ix2 < 0 || dd < td2)
+	    {
+	      if (fabs(tp1 - t1) > delta)
+		{
+		  ix2 = (int)kj;
+		  td2 = dd;
+		  t2 = tp1;
+		}
+	      else if ((ix2 < 0 || dd2 < td2) && fabs(tp2 - t1) > delta)
+		{
+		  ix2 = (int)kj;
+		  td2 = dd2;
+		  t2 = tp2;
+		}
+	    }
+	}
+
+      
+	  // if (ix1 < 0 || dd < td1)
+	  //   {
+	  //     if (ix1 < 0 || (ix2 >= 0 && td1 >= td2 && (!fabs(tp1 - t2) < eps)))
+	  // 	{
+	  // 	  ix1 = (int)kj;
+	  // 	  td1 = dd;
+	  // 	  t1 = tp1;
+	  // 	  set1 = true;
+	  // 	}
+	  //     else if (ix1 < 0 || (ix2 >= 0 && dd2 < td1 &&
+	  // 			   td1 >= td2 && (!fabs(tp2 - t2) < eps)))
+	  // 	{
+	  // 	  ix1 = (int)kj;
+	  // 	  td1 = dd2;
+	  // 	  t1 = tp2;
+	  // 	  set2 = true;
+	  // 	}
+	  //   }
+	  // if (ix2 < 0 || dd < td2)
+	  //   {
+	  //     if ((!set1) && ix1 >= 0 && td2 > td1 && (!fabs(tp1 - t1) < eps))
+	  // 	{
+	  // 	  ix2 = (int)kj;
+	  // 	  td2 = dd;
+	  // 	  t2 = tp1;
+	  // 	}
+	  //     else if ((!set2) && ix1 >= 0 && dd2 < td2 &&
+	  // 	       td2 > td1 && (!fabs(tp2 - t1) < eps))
+	  // 	{
+	  // 	  ix2 = (int)kj;
+	  // 	  td2 = dd2;
+	  // 	  t2 = tp2;
+	  // 	}
+	  //   }
+	  // if (ix1 < 0 || ((dd < td12-eps ||
+	  // 		   (dd < td12 && fabs(t2-tp1) > eps)) && td2 <= td1))
+	  //   {
+	  //     ix1 = (int)kj;
+	  //     td1 = dd;
+	  //     t1 = tp1;
+	  //   }
+	  // else if (ix2 < 0 || ((dd < td12-eps ||
+	  // 			(dd < td12 && fabs(t1-tp1) > eps)) && td1 < td2))
+	  //   {
+	  //     ix2 = (int)kj;
+	  //     td2 = dd;
+	  //     t2 = tp1;
+	  //   }
+
+	  // td12 = std::max(td1, td2);
+	  // if (dd2 < 0.0)
+	  //   dd2 = td12;
+	  // if (ix1 < 0 || ((dd2 < td12-eps ||
+	  // 		  (dd2 < td12 && fabs(t2-tp2) > eps)) && td2 <= td1))
+	  //   {
+	  //     ix1 = (int)kj;
+	  //     td1 = dd2;
+	  //     t1 = tp2;
+	  //   }
+	  // else if (ix2 < 0 || ((dd2 < td12-eps ||
+	  // 			(dd2 < td12 && fabs(t1-tp2) > eps)) && td1 < td2))
+	  //   {
+	  //     ix2 = (int)kj;
+	  //     td2 = dd2;
+	  //     t2 = (ki < kj) ? info[ki][kj].par3_ : info[ki][kj].par4_;
+	  //   }
+      //}
+      if (/*td1 > tol && td2 > tol &&*/ td0 < std::min(td1, td2))
+	{
+	  // Closed loop
+	  ix1 = ix2 = (int)ki;
+	  t1 = sfcv[ki]->startparam();
+	  t2 = sfcv[ki]->endparam();
+	}
+      
+      if (t2 < t1)
+	{
+	  std::swap(t1, t2);
+	  std::swap(ix1, ix2);
+	}
+      seq[ki] = std::make_pair(ix1, ix2);
+      param[ki] = std::make_pair(t1, t2);
+
+      if ((t1 > sfcv[ki]->startparam()+eps || t2 < sfcv[ki]->endparam()-eps) && t2 > t1)
+	{
+	  if ((td1 > tol || td2 > tol) &&
+	      std::min(t1-sfcv[ki]->startparam(), sfcv[ki]->endparam()-t2) < tol)
+	    {
+	      // Extra testing
+	      shared_ptr<ParamCurve> pcrv = sfcv[ki]->parameterCurve();
+	      double tpar = (t1-sfcv[ki]->startparam() > sfcv[ki]->endparam()-t2) ? t1 : t2;
+	      double t3 = 0.75*tpar + 0.25*pcrv->startparam();
+	      double t4 = 0.75*tpar + 0.25*pcrv->endparam();
+	      Point ppar1 = pcrv->point(t3);
+	      Point ppar2  = pcrv->point(t4);
+	      Point close1(std::max(domain_[0], std::min(domain_[1], ppar1[0])),
+			   std::max(domain_[2], std::min(domain_[3], ppar1[1])));
+	      Point close2(std::max(domain_[0], std::min(domain_[1], ppar2[0])),
+			   std::max(domain_[2], std::min(domain_[3], ppar2[1])));
+	      double d1 = ppar1.dist(close1);
+	      double d2 = ppar2.dist(close2);
+	      if (tpar == t1 && d1<d2)
+		{
+		  t2 = sfcv[ki]->startparam();
+		  std::swap(t1, t2);
+		  std::swap(ix1, ix2);
+		  seq[ki] = std::make_pair(ix1, ix2);
+		}
+	      else if (tpar == t2 && d2<d1)
+		{
+		  t1 = sfcv[ki]->endparam();
+		  std::swap(t1, t2);
+		  std::swap(ix1, ix2);
+		  seq[ki] = std::make_pair(ix1, ix2);
+		}
+	      int stop_d = 1;
+	    }
+	  shared_ptr<CurveOnSurface> sub(sfcv[ki]->subCurve(t1, t2));
+	  shared_ptr<ftEdge> subedge(new ftEdge(sub, t1, t2));
+	  ftEdgeBase *twin = trim_edgs_[ki]->twin();
+	  if (twin)
+	    {
+	      trim_edgs_[ki]->disconnectTwin();
+	      subedge->connectTwin(twin, status);
+	    }
+	  trim_edgs_[ki] = subedge;
+	}
+    }
+
+  // Could be necessary to remove extra entrances of closed edges
+  
+  vector<int> seq_ix;
+  vector<bool> turn;
+  seq_ix.push_back(0);
+  turn.push_back(false);
+  vector<size_t> start_ix;
+  start_ix.push_back(0);
+  while (seq_ix.size() < trim_edgs_.size())
+    {
+      size_t ix = seq_ix.size() - 1;
+      for (size_t ki=ix; ki<seq_ix.size(); ++ki)
+	{
+	  // Select next curve
+	  int ix1 = seq[seq_ix[ki]].first;
+	  int ix2 = seq[seq_ix[ki]].second;
+	  double t1 = param[seq_ix[ki]].first; 
+	  double t2 = param[seq_ix[ki]].second;
+	  Point pt1 = sfcv[seq_ix[ki]]->parameterCurve()->point(t1);
+	  Point pt2 = sfcv[seq_ix[ki]]->parameterCurve()->point(t2);
+	  for (size_t kr=0; kr<seq_ix.size(); ++kr)
+	    {
+	      if (seq_ix[kr] == ix1)
+		ix1 = -1;
+	      if (seq_ix[kr] == ix2)
+		ix2 = -1;
+	    }
+
+	  if (ix1 >= 0 && turn[ki])
+	    {
+	      seq_ix.push_back(ix1);
+	      Point pt3 = sfcv[ix1]->parameterCurve()->point(param[ix1].first);
+	      Point pt4 = sfcv[ix1]->parameterCurve()->point(param[ix1].second);
+	      if (pt1.dist(pt3) > pt1.dist(pt4))
+		turn.push_back(true);
+	      else
+		turn.push_back(false);
+	    }
+	  else if (ix2 >= 0 && (!turn[ki]))
+	    {
+	      seq_ix.push_back(ix2);
+	      Point pt3 = sfcv[ix2]->parameterCurve()->point(param[ix2].first);
+	      Point pt4 = sfcv[ix2]->parameterCurve()->point(param[ix2].second);
+	      if (pt2.dist(pt3) > pt2.dist(pt4))
+		turn.push_back(true);
+	      else
+		turn.push_back(false);
+	    }
+	}
+
+      for (size_t ki=seq_ix.size()-1; ki<seq_ix.size(); ++ki)
+	{
+	  // Select previous curve
+	  int ix1 = seq[seq_ix[ix]].first;
+	  int ix2 = seq[seq_ix[ix]].second;
+	  double t1 = param[seq_ix[ix]].first;
+	  double t2 = param[seq_ix[ix]].second;
+	  Point pt1 = sfcv[seq_ix[ix]]->parameterCurve()->point(t1);
+	  Point pt2 = sfcv[seq_ix[ix]]->parameterCurve()->point(t2);
+	  for (size_t kr=0; kr<seq_ix.size(); ++kr)
+	    {
+	      if (seq_ix[kr] == ix1)
+		ix1 = -1;
+	      if (seq_ix[kr] == ix2)
+		ix2 = -1;
+	    }
+
+	  if (ix1 >= 0 && (!turn[ix]))
+	    {
+	      seq_ix.insert(seq_ix.begin()+ix, ix1);
+	      Point pt3 = sfcv[ix1]->parameterCurve()->point(param[ix1].first);
+	      Point pt4 = sfcv[ix1]->parameterCurve()->point(param[ix1].second);
+	      if (pt1.dist(pt4) < pt1.dist(pt3)) 
+		turn.insert(turn.begin()+ix, false);
+	      else
+		turn.insert(turn.begin()+ix, true);
+	    }
+	  else if (ix2 >= 0 && turn[ix])
+	    {
+	      seq_ix.push_back(ix2);
+	      Point pt3 = sfcv[ix2]->parameterCurve()->point(param[ix2].first);
+	      Point pt4 = sfcv[ix2]->parameterCurve()->point(param[ix2].second);
+	      if (pt2.dist(pt3) > pt2.dist(pt4))
+		turn.insert(turn.begin()+ix, false);
+	      else
+		turn.insert(turn.begin()+ix, true);
+	    }
+	}
+      // Select unused edge
+      size_t kr, kh;
+      for (kr=0; kr<trim_edgs_.size(); ++kr)
+	{
+	  for (kh=0; kh<seq_ix.size(); ++kh)
+	    if (seq_ix[kh] == (int)kr)
+	      break;
+
+	  if (kh == seq_ix.size())
+	    {
+	      start_ix.push_back(seq_ix.size());
+	      seq_ix.push_back((int)kr);
+	      turn.push_back(false);
+	      break;
+	    }
+	}
+    }
+  start_ix.push_back(seq_ix.size());
+  // vector<int> seq_ix(trim_edgs_.size(), -1);
+  // vector<bool> turn(trim_edgs_.size(), false);
+  // seq_ix[0] = 0;
+  // for (size_t ki=0; ki<trim_edgs_.size()-1; ++ki)
+  //   {
+  //     // Select next curve
+  //     int ix1 = seq[seq_ix[ki]].first;
+  //     int ix2 = seq[seq_ix[ki]].second;
+  //     double t1 = param[seq_ix[ki]].first; //(seq_ix[ki] < ix1) ? info[seq_ix[ki]][ix1].par1_ : info[seq_ix[ki]][ix1].par2_;
+  //     double t2 = param[seq_ix[ki]].second; //(seq_ix[ki] < ix2) ? info[seq_ix[ki]][ix2].par1_ : info[seq_ix[ki]][ix2].par2_;
+  //     Point pt1 = sfcv[ki]->ParamCurve::point(t1);
+  //     Point pt2 = sfcv[ki]->ParamCurve::point(t2);
+  //     for (size_t kr=0; kr<ki; ++kr)
+  // 	{
+  // 	  if (seq_ix[kr] == ix1)
+  // 	    ix1 = -1;
+  // 	  if (seq_ix[kr] == ix2)
+  // 	    ix2 = -1;
+  // 	}
+  //     if (ix1 != ix2 && ix1 >= 0 && ((turn[ki] && t1<t2) || ((!turn[ki]) && t2<t1)))
+  // 	{
+  // 	  seq_ix[ki+1] = ix1;
+  // 	  //double t3 = (seq_ix[ki] < ix1) ? info[seq_ix[ki]][ix1].par2_ : info[seq_ix[ki]][ix1].par1_;
+  // 	  //if (fabs(sfcv[ix1]->endparam()-t3) < fabs(t3-sfcv[ix1]->startparam()))
+  // 	  Point pt3 = sfcv[ix1]->ParamCurve::point(sfcv[ix1]->startparam());
+  // 	  Point pt4 = sfcv[ix1]->ParamCurve::point(sfcv[ix1]->endparam());
+  // 	  if (pt1.dist(pt4)+pt2.dist(pt3) < pt1.dist(pt3)+pt2.dist(pt4))
+  // 	    turn[ki+1] = true;
+  // 	}
+  //     else if (ix1 != ix2 && ix2 >= 0)
+  // 	{
+  // 	  seq_ix[ki+1] = ix2;
+  // 	  //double t3 = (seq_ix[ki] < ix2) ? info[seq_ix[ki]][ix2].par2_ : info[seq_ix[ki]][ix2].par1_;
+  // 	  //if (fabs(sfcv[ix2]->endparam()-t2) < fabs(t3-sfcv[ix2]->startparam()))
+  // 	  Point pt3 = sfcv[ix2]->ParamCurve::point(sfcv[ix2]->startparam());
+  // 	  Point pt4 = sfcv[ix2]->ParamCurve::point(sfcv[ix2]->endparam());
+  // 	  if (pt1.dist(pt4)+pt2.dist(pt3) < pt1.dist(pt3)+pt2.dist(pt4))
+  // 	    turn[ki+1] = true;
+  // 	}
+  //     else
+  // 	{
+  // 	  // Select unused edge
+  // 	  size_t kr, kh;
+  // 	  for (kr=0; kr<trim_edgs_.size(); ++kr)
+  // 	    {
+  // 	      for (kh=0; kh<=ki; ++kh)
+  // 		if (seq_ix[kh] == (int)kr)
+  // 		  break;
+  // 	      if (kh > ki)
+  // 		{
+  // 		  seq_ix[ki+1] = (int)kr;
+  // 		  turn[ki+1] = false;
+  // 		  break;
+  // 		}
+  // 	    }
+  // 	}
+  //   }
+
+  vector<shared_ptr<ftEdge> > tmp_edgs(trim_edgs_.size());
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    tmp_edgs[ki] = trim_edgs_[seq_ix[ki]];
+  std::swap(trim_edgs_, tmp_edgs);
+  
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    if (turn[ki])
+      trim_edgs_[ki]->reverseGeomCurve();
+
+#ifdef DEBUG_TRIM
+  std::ofstream of0("trim_edgs_arrange0.g2");
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      shared_ptr<CurveOnSurface> sfcv =
+	dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+      if (!sfcv.get())
+	continue;
+      shared_ptr<ParamCurve> space = sfcv->spaceCurve();
+      if (!space.get())
+	continue;
+      space->writeStandardHeader(of0);
+      space->write(of0);
+      Point startpt = space->point(space->startparam());
+      of0 << "400 1 0 4 255 0 0 255" << std::endl;
+      of0 << "1" << std::endl;
+      of0 << startpt << std::endl;
+    }
+#endif
+  
+  HedgeSurface *hedge = getSurface(0);
+  shared_ptr<ParamSurface> surf = hedge->surface();
+  double lenfac = 0.6;
+  double tol4 = 4.0*tol;
+  for (size_t ki=1; ki<start_ix.size(); ++ki)
+    {
+      size_t kj, kr;
+      for (kj=start_ix[ki-1]; kj<start_ix[ki]; ++kj)
+	{
+	  kr = (start_ix[ki]-start_ix[ki-1] == 1) ? kj :
+	    ((kj == start_ix[ki]-1) ? start_ix[ki-1] : kj+1);
+
+	  // Check distance between adjacent curve segments
+	  Point pt1 = trim_edgs_[kj]->point(trim_edgs_[kj]->tMax());
+	  Point pt2 = trim_edgs_[kr]->point(trim_edgs_[kr]->tMin());
+	  double glen = pt1.dist(pt2);
+	  if (glen > tol)
+	    {
+	      // Define missing edge
+	      shared_ptr<CurveOnSurface> sfcv1 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[kj]->geomCurve());
+	      shared_ptr<CurveOnSurface> sfcv2 =
+		dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[kr]->geomCurve());
+	      if ((!sfcv1.get()) || (!sfcv2.get()))
+		continue;
+
+	      double clen = sfcv1->estimatedCurveLength();
+	      if (kj == kr && (!trim_edgs_[kj]->twin()))
+		{
+		  // Single curve
+		  if (glen > lenfac*clen)
+		    {
+		      // Remove trimming curve
+		      trim_edgs_.erase(trim_edgs_.begin()+kj);
+		      for (size_t kh=ki+1; kh<start_ix.size(); ++kh)
+			start_ix[kh]--;
+		      start_ix.erase(start_ix.begin()+ki);
+		      --ki;
+		      break;
+		    }
+		  
+		}
+
+	      if (clen - glen < tol4)
+		continue;   // Don't duplicate an almost straight curve
+	      
+	      shared_ptr<ParamCurve> pcv1 = sfcv1->parameterCurve();
+	      shared_ptr<ParamCurve> pcv2 = sfcv2->parameterCurve();
+	      if ((!pcv1.get()) || (!pcv2.get()))
+		continue;
+
+	      Point ppos1 = pcv1->point(pcv1->endparam());
+	      Point ppos2 = pcv2->point(pcv2->startparam());
+	      if (ppos1.dist(ppos2) < eps)
+		continue;
+	      shared_ptr<SplineCurve> gap_par(new SplineCurve(ppos1, ppos2));
+	      shared_ptr<CurveOnSurface> gap_sfcv(new CurveOnSurface(surf,
+								     gap_par,
+								     true));
+	      gap_sfcv->ensureSpaceCrvExistence(tol);
+	      shared_ptr<ftEdge> gap_edge(new ftEdge(hedge, gap_sfcv,
+						     gap_sfcv->startparam(),
+						     gap_sfcv->endparam()));
+	      trim_edgs_.insert(trim_edgs_.begin()+kr, gap_edge);
+
+	      for (size_t kh=ki; kh<start_ix.size(); ++kh)
+		start_ix[kh]++;
+	      ++kj;
+	    }
+	}
+    }
+  
+#ifdef DEBUG_TRIM
+  std::ofstream of1("trim_edgs_arrange.g2");
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      shared_ptr<CurveOnSurface> sfcv =
+	dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+      if (!sfcv.get())
+	continue;
+      shared_ptr<ParamCurve> space = sfcv->spaceCurve();
+      if (!space.get())
+	continue;
+      space->writeStandardHeader(of1);
+      space->write(of1);
+      Point startpt = space->point(space->startparam());
+      of1 << "400 1 0 4 255 0 0 255" << std::endl;
+      of1 << "1" << std::endl;
+      of1 << startpt << std::endl;
+    }
+#endif
+  
+  int stop_break = 1;
+  return true;
+}
+
+//===========================================================================
+void RevEngRegion::extractPointsAtSeam(vector<RevEngPoint*>& seam_pts1,
+				       vector<RevEngPoint*>& seam_pts2, bool along_udir)
+//===========================================================================
+{
+  // Investigate only the points close to the domain boundary
+  int p_ix = along_udir ? 0 : 1;
+  double start = domain_[2*p_ix];
+  double end = start + 0.1*(domain_[2*p_ix+1] - domain_[2*p_ix]);
+  double end2 = domain_[2*p_ix+1];
+  double start2 = end2 - 0.1*(domain_[2*p_ix+1] - domain_[2*p_ix]);
+  std::set<RevEngPoint*> tmp_pts2;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector2D uv = group_points_[ki]->getPar();
+      if (uv[p_ix] >= start && uv[p_ix] <= end)
+	{
+	  bool found = false;
+	  vector<ftSamplePoint*> next = group_points_[ki]->getNeighbours();
+	  for (size_t kj=0; kj<next.size(); ++kj)
+	    {
+	      RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	      Vector2D uv2 = curr->getPar();
+	      if (uv2[p_ix] >= start2 && uv2[p_ix] <= end2)
+		{
+		  tmp_pts2.insert(curr);
+		  found = true;
+		}
+	    }
+	  if (found)
+	    seam_pts1.push_back(group_points_[ki]);
+	}
+    }
+  if (tmp_pts2.size() > 0)
+    {
+      vector<RevEngPoint*> tmp2_pts2(tmp_pts2.begin(), tmp_pts2.end());
+      seam_pts2 = tmp2_pts2;
+    }
+}
+
+//===========================================================================
+void RevEngRegion::getAdjCandMerge(vector<RevEngRegion*>& adj_surf,
+				   vector<RevEngRegion*>& adj_nosurf)
+//===========================================================================
+{
+  vector<RevEngRegion*> adj_reg;
+  adj_reg.insert(adj_reg.end(), adjacent_regions_.begin(), adjacent_regions_.end());
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    for (size_t kj=ki+1; kj<adj_reg.size(); ++kj)
+      if (adj_reg[kj]->numPoints() > adj_reg[ki]->numPoints())
+	std::swap(adj_reg[ki], adj_reg[kj]);
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    
+    {
+      if (adj_reg[ki]->prev_region_ && adj_reg[ki]->prev_region_ == this)
+	continue;
+      if (commonRevEdge(adj_reg[ki]) || adj_reg[ki]->hasAssociatedBlend() ||
+	  adj_reg[ki]->hasBlendEdge())
+	continue;
+
+      if (adj_reg[ki]->hasSurface())
+	{
+	  // Check compatibility (todo)
+	  adj_surf.push_back(adj_reg[ki]);
+	}
+      else
+	{
+	  // Check compatibility (todo)
+	  adj_nosurf.push_back(adj_reg[ki]);
+	}
+    }
+}
+
+//===========================================================================
+bool RevEngRegion::commonRevEdge(RevEngRegion *other)
+//===========================================================================
+{
+  vector<RevEngEdge*> other_edgs = other->getAllRevEdges();
+  for (size_t ki=0; ki<rev_edges_.size(); ++ki)
+    for (size_t kj=0; kj<other_edgs.size(); ++kj)
+      if (rev_edges_[ki] == other_edgs[kj])
+	return true;
+
+  return false;
+}
+
+//===========================================================================
+bool RevEngRegion::commonTrimEdge(RevEngRegion *other)
+//===========================================================================
+{
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      ftEdgeBase *twin0 = trim_edgs_[ki]->twin();
+      if (!twin0)
+	continue;
+      ftEdge *twin = twin0->geomEdge();
+      ftFaceBase *face0 = twin->face();
+      if (!face0)
+	continue;
+      HedgeSurface *hedge = dynamic_cast<HedgeSurface*>(face0);
+      if (!hedge)
+	continue;
+      RevEngRegion *reg = hedge->getRegion(0);
+      if (reg == other)
+	return true;
+    }
+  return false;
+}
+
+//===========================================================================
+void RevEngRegion::adaptEdges()
+//===========================================================================
+{
+  if (associated_sf_.size() > 0)
+    {
+      RectDomain dom = associated_sf_[0]->surface()->containingDomain();
+      double dom2[4];
+      dom2[0] = dom.umin();
+      dom2[1] = dom.umax();
+      dom2[2] = dom.vmin();
+      dom2[3] = dom.vmax();
+      for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+	{
+	  adaptOneEdge(trim_edgs_[ki], dom2);
+	}
+    }
+}
+
+//===========================================================================
+void RevEngRegion::adaptOneEdge(shared_ptr<ftEdge>& edge, double dom[4])
+//===========================================================================
+{
+  int status = 0;
+  double eps = 1.0e-9;
+  shared_ptr<CurveOnSurface> sfcv =
+    dynamic_pointer_cast<CurveOnSurface,ParamCurve>(edge->geomCurve());
+  if (!sfcv.get())
+    return;
+  if (sfcv->isConstantCurve())
+    {
+      int dir, bd;
+      double val;
+      bool orient;
+      sfcv->getConstantCurveInfo(dir, val, bd, orient);
+      shared_ptr<ParamCurve> pcurve = sfcv->parameterCurve();
+      if (pcurve.get())
+	{
+	  double t1 = pcurve->startparam();
+	  double t2 = pcurve->endparam();
+	  Point pt1 = pcurve->point(t1);
+	  Point pt2 = pcurve->point(t2);
+	  double tpar, dist;
+	  Point close;
+	  int ix = (dir == 1) ? 1 : 0;
+	  double tmax = std::max(pt1[ix], pt2[ix]);
+	  double tmin = std::min(pt1[ix], pt2[ix]);
+	  if (tmax > dom[2*ix+1]+eps)
+	    {
+	      Point pos;
+	      pos = (ix == 0) ? Point(dom[2*ix+1], val) : Point(val, dom[2*ix+1]);
+	      pcurve->closestPoint(pos, t1, t2, tpar, close, dist);
+	      t2 = tpar;
+	    }
+	  if (tmin < dom[2*ix]-eps)
+	    {
+	      Point pos;
+	      pos = (ix == 0) ? Point(dom[2*ix], val) : Point(val, dom[2*ix]);
+	      pcurve->closestPoint(pos, t1, t2, tpar, close, dist);
+	      t1 = tpar;
+	    }
+
+	  if (t1 > pcurve->startparam() || t2 < pcurve->endparam())
+	    {
+	      shared_ptr<CurveOnSurface> sub(sfcv->subCurve(t1, t2));
+	      shared_ptr<ftEdge> subedge(new ftEdge(edge->face(), sub, t1, t2));
+	      ftEdgeBase *twin = edge->twin();
+	      edge->disconnectTwin();
+	      subedge->connectTwin(twin, status);
+	      edge = subedge;
+	    }
+	}
+      else
+	MESSAGE("adaptCurve, missing parameter curve");
+    }
+  else
+    MESSAGE("adaptCurve for non-constant trimming curves is not implemented");
+}
+
+//===========================================================================
+void RevEngRegion::getAdjacentBlends(vector<RevEngRegion*>& adj_blends)
+//===========================================================================
+{
+  for (auto it=adjacent_regions_.begin(); it!= adjacent_regions_.end(); ++it)
+    {
+      if ((*it)->hasBlendEdge())
+	adj_blends.push_back(*it);
+    }
+}
+
+//===========================================================================
+shared_ptr<SplineSurface> RevEngRegion::updateFreeform(vector<RevEngPoint*>& points,
+						       double tol)
+//===========================================================================
+{
+  shared_ptr<SplineSurface> dummy;
+  if (associated_sf_.size() == 0)
+    return dummy;
+
+  shared_ptr<ParamSurface> surf0 = associated_sf_[0]->surface();
+  shared_ptr<SplineSurface> surf =
+    dynamic_pointer_cast<SplineSurface,ParamSurface>(surf0);
+  if (!surf.get())
+    return dummy;
+
+  vector<double> data, param;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Vector2D uv = points[ki]->getPar();
+      data.insert(data.end(), xyz.begin(), xyz.end());
+      param.insert(param.end(), uv.begin(), uv.end());
+    }
+
+  int dim = surf->dimension();
+  ApproxSurf approx(surf, data, param, dim, tol);
+  //ApproxSurf approx(surf3, data, param, dim, tol, 0, false, true, 0, true);
+  approx.setMBA(true);
+  approx.setFixBoundary(false);
+  int max_iter = 1;
+  double maxd, avd;
+  int num_out;
+  shared_ptr<SplineSurface> surf2;
+  try {
+    surf2 = approx.getApproxSurf(maxd, avd, num_out, max_iter);
+  }
+  catch (...)
+  {
+    std::cout << "Surface update failed" << std::endl;
+  }
+  
+ return surf2;
+}
+  
+
+//===========================================================================
+shared_ptr<SplineSurface> RevEngRegion::computeFreeform(vector<RevEngPoint*>& points,
+							double tol)
+//===========================================================================
+{
+// Parameterize
+  vector<double> data, param;
+  int inner1=0, inner2=0;
+      
+  // Compute PCA axes
+  double lambda[3];
+  Point eigen1, eigen2, eigen3;
+  getPCA(lambda, eigen1, eigen2, eigen3);
+
+  //bool usebasesf = false;
+  bool done = false;
+  bool close1 = false, close2 = false;
+  if (associated_sf_.size() > 0)
+    {
+      done = parameterizeOnSurf(group_points_,
+				associated_sf_[0]->surface(), data,
+				param, inner1, inner2, close1, close2);
+    }
+  if (!done && basesf_.get() && avdist_base_ <= 5.0*tol)
+    {
+      done = parameterizeOnSurf(group_points_, basesf_, data, param, 
+				inner1, inner2, close1, close2);
+    }
+  if (!done)
+    {
+     // Parameterize on plane
+      RevEngUtils::parameterizeWithPlane(points, bbox_, eigen1,
+					 eigen2, data, param);
+    }
+#ifdef DEBUG_EXTRACT
+  std::ofstream ofpar("parpoints.g2");
+  int nmbpar = (int)param.size()/2;
+  ofpar << "400 1 0 0" << std::endl;
+  ofpar << nmbpar << std::endl;
+  for (int ka=0; ka<nmbpar; ++ka)
+    ofpar << param[2*ka] << " " << param[2*ka+1] << "  0.0" << std::endl;
+#endif
+  
+  vector<double> param2;
+  double umin, umax, vmin, vmax;
+  bool repar = reparameterize(param, param2, umin, umax, vmin, vmax);
+  //std::cout << "repar: " << repar << std::endl;
+  if (repar)
+    {
+      vector<double> p_edg1, p_edg2;
+      for (size_t ki=0; ki<points.size(); ++ki)
+	{
+	  vector<ftSamplePoint*> next = points[ki]->getNeighbours();
+	  for (size_t kj=0; kj<next.size(); ++kj)
+	    {
+	      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next[kj]);
+	      if (pt->region() != this)
+		continue;
+	      std::vector<RevEngPoint*>::iterator it = std::find(points.begin(),
+								 points.end(), pt);
+	      if (it != points.end())
+		{
+		  int ix = (int)(it - points.begin());
+		  p_edg1.push_back(param[2*ki]);
+		  p_edg1.push_back(param[2*ki+1]);
+		  p_edg2.push_back(param2[2*ki]);
+		  p_edg2.push_back(param2[2*ki+1]);
+		  p_edg1.push_back(param[2*ix]);
+		  p_edg1.push_back(param[2*ix+1]);
+		  p_edg2.push_back(param2[2*ix]);
+		  p_edg2.push_back(param2[2*ix+1]);
+		}
+	    }
+	}
+
+#ifdef DEBUG_EXTRACT
+      std::ofstream ofe1("par_edgs1.g2");
+      std::ofstream ofe2("par_edgs2.g2");
+      ofe1 << "410 1 0 4 255 0 0 255" << std::endl;
+      ofe2 << "410 1 0 4 255 0 0 255" << std::endl;
+      ofe1 << p_edg1.size()/4 << std::endl;
+      ofe2 << p_edg2.size()/4 << std::endl;
+      for (size_t ki=0; ki<p_edg1.size(); ki+=4)
+	{
+	  ofe1 << p_edg1[ki] << " " << p_edg1[ki+1] << " 0.0 " << p_edg1[ki+2];
+	  ofe1 << " " << p_edg1[ki+3] << " 0.0" << std::endl;
+	  ofe2 << p_edg2[ki] << " " << p_edg2[ki+1] << " 0.0 " << p_edg2[ki+2];
+	  ofe2 << " " << p_edg2[ki+3] << " 0.0" << std::endl;
+	}
+#endif
+      
+      double plenfac = 5.0;
+      if (umax - umin > plenfac*(vmax - vmin))
+	{
+	  if (inner1 <= inner2)
+	    inner1 = inner2 + 1;
+	}
+      else if (vmax - vmin > plenfac*(umax - umin))
+	{
+	  if (inner2 <= inner1)
+	    inner2 = inner1 + 1;
+	}
+      std::swap(param, param2);
+    }
+
+  // Extend with extra points in corners far from the point cloud
+  //size_t nmb_prev_extend = param.size();
+  if ((!close1) && (!close2))
+    {
+      try {
+	extendInCorner(data, param, umin, umax, vmin, vmax);
+      }
+      catch (...)
+	{
+	  std::cout << "Corner extend failed" << std::endl;
+	}
+    }
+  
+  // Approximate
+  double maxd, avd;
+  int num_out;
+  int max_iter = (associated_sf_.size() > 0) ? 3 : 6; //2;
+  int dim = bbox_.low().dimension();
+  int order = 4;
+  shared_ptr<SplineSurface> surf;
+  double del = 0.01;
+  vector<double> parvals;
+  try {
+    surf = RevEngUtils::surfApprox(data, dim, param, order, order, 
+				   order+inner1,  order+inner2,
+				   close1, close2, max_iter, 
+				   tol, maxd, avd, num_out, parvals, del);
+  }
+  catch (...)
+    {
+#ifdef DEBUG_EXTRACT      
+       std::cout << "Surface approximation failed" << std::endl;
+#endif
+   }
+
+#ifdef DEBUG_EXTRACT
+  if (surf.get())
+    {
+      std::ofstream of("spline_approx.g2");
+      surf->writeStandardHeader(of);
+      surf->write(of);
+    }
+  else
+    int no_surf = 1;
+#endif
+  
+  return surf;
+}
+											 
+int compare_u_par(const void* el1, const void* el2)
+{
+  if (((double*)el1)[0] < ((double*)el2)[0])
+    return -1;
+  else if (((double*)el1)[0] > ((double*)el2)[0])
+    return 1;
+  else
+    return 0;
+}
+
+int compare_v_par(const void* el1, const void* el2)
+{
+  if (((double*)el1)[1] < ((double*)el2)[1])
+    return -1;
+  else if (((double*)el1)[1] > ((double*)el2)[1])
+    return 1;
+  else
+    return 0;
+}
+
+//===========================================================================
+bool RevEngRegion::reparameterize(vector<double>& param, vector<double>& param2,
+				  double& umin, double& umax, double& vmin, double& vmax)
+//===========================================================================
+{
+  // Define raster
+  int nmb_div = 15;
+  vector<vector<int> > raster;
+  vector<double> param_copy(param.begin(), param.end());  // Raster definition
+  // changes sequence of parameter points
+  defineRaster(param_copy, nmb_div, raster, umin, umax, vmin, vmax);
+  int div2 = (int)raster.size();
+  if (div2 == 0)
+    return false;
+  int div1 = (int)raster[0].size();
+  double udel = (umax - umin)/(double)(div1);
+  double vdel = (vmax - vmin)/(double)(div2);
+
+  // Count number of empty cells
+  int nmb_zero = 0;
+  for (int kb=0; kb<div2; ++kb)
+    for (int ka=0; ka<div1; ++ka)
+      if (raster[kb][ka] == 0)
+	++nmb_zero;
+
+  if (nmb_zero < (int)(0.5*div1*div2))
+    return false;
+
+  // Identify points on edges
+  double eps = 1.0e-10;
+  vector<double> edge_pts;
+  for (size_t kr=0; kr<param.size(); kr+=2)
+    {
+      if (param[kr]-umin < eps || umax-param[kr] < eps)
+	{
+	  edge_pts.push_back((param[kr]-umin < eps) ? 0.0 : (double)div1);
+	  edge_pts.push_back(0.5*(int)(2*(param[kr+1]-vmin)/vdel));
+	}
+      if (param[kr+1]-vmin < eps || vmax-param[kr+1] < eps)
+	{
+	  edge_pts.push_back(0.5*(int)(2*(param[kr]-umin)/udel));
+	  edge_pts.push_back((param[kr+1]-vmin < eps) ? 0.0 : (double)div2);
+	}
+    }
+
+  // Find candidates for endpoints of a mid curve through the paramaeter points
+  vector<Point> cand_par;
+  vector<double> wd;
+  int i1, i2, i3, i4, ix, kc;
+  for (kc=0, ix=0; kc<2; ++kc, ix=div2-1)
+    for (i1=i2=0; i1<div1; i1=i2)
+      {
+	for (; i1<div1; ++i1)
+	  if (raster[ix][i1] > 0)
+	    break;
+	for (i2=i1; i2<div1; ++i2)
+	  if (raster[ix][i2] == 0)
+	    break;
+	if (i2 > i1)
+	  {
+	    size_t kr;
+	    for (kr=0; kr<edge_pts.size(); kr+=2)
+	      {
+		if (((kc == 0 && edge_pts[kr+1] == 0) ||
+		     (kc == 1 && edge_pts[kr+1] == div2)) &&
+		    (edge_pts[kr] >= i1 && edge_pts[kr] <= i2))
+		  break;
+	      }
+	    
+	    if (kr < edge_pts.size())
+	      {
+		Point currpar(2);
+		currpar[0] = edge_pts[kr]; //0.5*(i1+i2);
+		currpar[1] = (kc == 0) ? 0 : (double)div2;
+		cand_par.push_back(currpar);
+		wd.push_back(0.5*udel*(i2-i1));
+	      }
+	  }
+      }
+
+  for (kc=0, ix=0; kc<2; ++kc, ix=div1-1)
+    for (i1=i2=0; i1<div2; i1=i2)
+      {
+	for (; i1<div2; ++i1)
+	  if (raster[i1][ix] > 0)
+	    break;
+	for (i2=i1; i2<div2; ++i2)
+	  if (raster[i2][ix] == 0)
+	    break;
+	if (i2 > i1)
+	  {
+	    size_t kr;
+	    for (kr=0; kr<edge_pts.size(); kr+=2)
+	      {
+		if (((kc == 0 && edge_pts[kr] == 0) ||
+		     (kc == 1 && edge_pts[kr] == div1)) &&
+		    (edge_pts[kr+1] >= i1 && edge_pts[kr+1] <= i2))
+		  break;
+	      }
+	    
+	    if (kr < edge_pts.size())
+	      {
+		Point currpar(2);
+		currpar[0] = (kc == 0) ? 0 : (double)div1;
+		currpar[1] = edge_pts[kr+1]; //0.5*(i1+i2);
+		cand_par.push_back(currpar);
+		wd.push_back(0.5*vdel*(i2-i1));
+	      }
+	  }
+      }
+
+  if (cand_par.size() < 2)
+    return false;
+  
+  // Decide on endpoints
+  int ix1=-1, ix2=-1;
+  double maxd = std::numeric_limits<double>::lowest();
+  for (size_t kr=0; kr<cand_par.size(); ++kr)
+    for (size_t kh=kr+1; kh<cand_par.size(); ++kh)
+      {
+	double dd = cand_par[kr].dist(cand_par[kh]);
+	dd -= (wd[kr] + wd[kh]);
+	if (dd > maxd)
+	  {
+	    ix1 = (int)kr;
+	    ix2 = (int)kh;
+	    maxd = dd;
+	  }
+      }
+
+  // Compute points along a sceleton in the parameter point cloud
+  double curr1[2], curr2[2];
+  double wd1, wd2;
+  vector<double> ptpar;
+  vector<double> width;
+  ptpar.push_back(cand_par[ix1][0]);
+  ptpar.push_back(cand_par[ix1][1]);
+  width.push_back(wd[ix1]);
+
+  Point prev;
+  int sgn1=0, sgn2=0;
+  bool loop = false;
+  for (kc=0; kc<(int)ptpar.size(); kc+=2)
+    {
+#ifdef DEBUG_REPAR
+      std::ofstream ofc("midpol.g2");
+      ofc << "410 1 0 4 0 0 0 255" << std::endl;
+      ofc << ptpar.size()/2 << std::endl;
+      ofc << umin+udel*ptpar[0] << " " << vmin+vdel*ptpar[1] << "  0.0" << std::endl;
+      for (size_t kr=2; kr<ptpar.size(); kr+=2)
+	{
+	  ofc << umin+udel*ptpar[kr] << " " << vmin+vdel*ptpar[kr+1] << "  0.0" << std::endl;
+	  ofc << umin+udel*ptpar[kr] << " " << vmin+vdel*ptpar[kr+1] << "  0.0" << std::endl;
+	}
+      ofc << umin+udel*cand_par[ix2][0] << " " << vmin+vdel*cand_par[ix2][1] << " 0.0" << std::endl;
+#endif
+      curr1[0] = curr2[0] = ptpar[kc];
+      curr1[1] = curr2[1] = ptpar[kc+1];
+      if (sgn1 == 0 || curr1[0] == prev[0])
+	sgn1 = (cand_par[ix2][0] > curr1[0]) ? 1 : -1;
+      if (sgn2 == 0 || curr2[1] == prev[1])
+	sgn2 = (cand_par[ix2][1] > curr2[1]) ? 1 : -1;
+      double fac1 = (curr1[0] == 0.0) ? 0.5 : 1.0;
+      double fac2 = (curr2[1] == 0.0) ? 0.5 : 1.0;
+      curr1[0] += fac1*sgn1;
+      curr2[1] += fac2*sgn2;
+      prev = Point(ptpar[kc], ptpar[kc+1]);
+      if (prev.dist(cand_par[ix2]) <= 1.0)
+	break;
+      curr1[0] = std::min(curr1[0], div1-0.5);
+      curr1[1] = std::min(curr1[1], div2-0.5);
+      curr2[0] = std::min(curr2[0], div1-0.5);
+      curr2[1] = std::min(curr2[1], div2-0.5);
+      curr1[0] = std::max(curr1[0], 0.0);
+      curr1[1] = std::max(curr1[1], 0.0);
+      curr2[0] = std::max(curr2[0], 0.0);
+      curr2[1] = std::max(curr2[1], 0.0);
+
+      // Extent with constant first parameter direction
+      getParExtent(curr1, 0, raster, i1, i2);
+      curr1[1] = 0.5*(double)(i1+i2+1);
+      wd1 = 0.5*(i1 - i2);
+
+      // Constant second parameter direction
+      getParExtent(curr2, 1, raster, i3, i4);
+      curr2[0] = 0.5*(double)(i3+i4+1);
+      wd2 = 0.5*(i3 - i4);
+
+      // Select continuation
+      Point lastpar(ptpar[ptpar.size()-2], ptpar[ptpar.size()-1]);
+      Point c1(curr1[0], curr1[1]);
+      Point c2(curr2[0], curr2[1]);
+      if (i1-i2 < i3-i4 || c2.dist(lastpar) < eps) 
+	{
+	  ptpar.push_back(curr1[0]);
+	  ptpar.push_back(curr1[1]);
+	  width.push_back(udel*wd1);
+	  lastpar = c1;
+	}
+      else
+	{
+	  ptpar.push_back(curr2[0]);
+	  ptpar.push_back(curr2[1]);
+	  width.push_back(vdel*wd2);
+	  lastpar = c2;
+	}
+	
+      // Check for loops
+      for (size_t kh=2; kh<ptpar.size()-2; kh+=2)
+	{
+	  Point currpar(ptpar[kh], ptpar[kh+1]);
+	  if (currpar.dist(lastpar) < eps)
+	    {
+	      loop = true;
+	      break;
+	    }
+	}
+      if (loop)
+	break;
+      
+      int stop_break0 = 1;      
+    }
+  if (loop)
+    return false;
+  
+  Point last(ptpar[ptpar.size()-2], ptpar[ptpar.size()-1]);
+  if (last.dist(cand_par[ix2]) > 0.0)
+    {
+      ptpar.push_back(cand_par[ix2][0]);
+      ptpar.push_back(cand_par[ix2][1]);
+      width.push_back(wd[ix2]);
+    }
+
+  if (ptpar.size() <= 2)
+    return false;
+  
+  // Translate to real coordinates
+  vector<double> ptpar2(ptpar.size());
+  for (size_t kr=0; kr<ptpar.size(); kr+=2)
+    {
+      ptpar2[kr] = umin+udel*ptpar[kr];
+      ptpar2[kr+1] = vmin+vdel*ptpar[kr+1];
+    }
+
+  // Extend
+  double npar1[2], npar2[2];
+  for (int ka=0; ka<2; ++ka)
+    {
+      npar1[ka] = ptpar2[ka] - 0.1*(ptpar2[2+ka]-ptpar2[ka]);
+      npar2[ka] = ptpar2[ptpar2.size()-2+ka] + 0.1*(ptpar2[ptpar2.size()-2+ka]-
+						   ptpar2[ptpar2.size()-4+ka]);
+    }
+  ptpar2.insert(ptpar2.begin(), &npar1[0], &npar1[0]+2);
+  ptpar2.insert(ptpar2.end(), &npar2[0], &npar2[0]+2);
+  width.insert(width.begin(), width[0]);
+  width.push_back(width[width.size()-1]);
+  
+
+#ifdef DEBUG_REPAR
+  std::ofstream ofp("parpt.g2");
+  ofp << "400 1 0 4 200 55 0 255" << std::endl;
+  ofp << ptpar2.size()/2 << std::endl;
+  for (size_t kr=0; kr<ptpar2.size(); kr+=2)
+    ofp << ptpar2[kr] << " " << ptpar2[kr+1] << " 0.0" << std::endl;
+#endif
+  
+  // Define "mid" curve
+  vector<double> par2(ptpar2.size()/2);
+  par2[0] = 0.0;
+  for (size_t kr=2; kr<ptpar2.size(); kr+=2)
+    {
+      double dd = Utils::distance_squared(&ptpar2[kr-2], &ptpar2[kr], &ptpar2[kr]);
+      par2[kr/2] = par2[(kr-2)/2] + sqrt(dd);
+    }
+
+  int in = 4, ik = 4;
+  int dim = 2;
+  double tol = 0.1;
+  ApproxCurve approx(ptpar2, par2, dim, tol, in, ik);
+  
+  double maxdist, avdist;
+  shared_ptr<SplineCurve> midcv = approx.getApproxCurve(maxdist, avdist, 1);
+
+  ApproxCurve approx2(width, par2, 1, tol, in, ik);
+  
+  double maxdistw, avdistw;
+  shared_ptr<SplineCurve> widthcv = approx2.getApproxCurve(maxdistw, avdistw, 1);
+
+#ifdef DEBUG_REPAR
+  std::ofstream ofmid("midcv.g2");
+  midcv->writeStandardHeader(ofmid);
+  midcv->write(ofmid);
+#endif
+  
+  double avw = 0.0;
+  for (size_t kr=0; kr<width.size(); ++kr)
+    avw += width[kr];
+  avw /= (double)width.size();
+
+  param2.resize(param.size());
+  double tmin = midcv->startparam();
+  double tmax = midcv->endparam();
+  for (size_t kr=0; kr<param.size(); kr+=2)
+    {
+      Point currpar(param[kr], param[kr+1]);
+      double tpar, dist;
+      Point close;
+      midcv->closestPoint(currpar, tmin, tmax, tpar, close, dist);
+
+      vector<Point> der(2);
+      midcv->point(der, tpar, 1);
+      Point vec = close - currpar;
+      Point vec2(-vec[1], vec[0]);
+      //double tmp = fabs(der[1]*vec);
+      // if (tmp > 1.0e-6)
+      // 	std::cout << "inner: " << tmp << std::endl;
+      if (vec2*der[1] < 0.0)
+	dist *= -1.0;
+      Point wdt = widthcv->ParamCurve::point(tpar);
+      //dist *= (avw/wdt[0]);
+      param2[kr] = tpar;
+      param2[kr+1] = dist;
+      int stop_break0 = 1;
+    }
+  
+#ifdef DEBUG_REPAR
+  std::ofstream ofpar2("parpoints_repar.g2");
+  int nmbpar = (int)param2.size()/2;
+  ofpar2 << "400 1 0 0" << std::endl;
+  ofpar2 << nmbpar << std::endl;
+  for (int ka=0; ka<nmbpar; ++ka)
+    ofpar2 << param2[2*ka] << " " << param2[2*ka+1] << "  0.0" << std::endl;
+#endif
+  
+  vector<vector<int> > raster2;
+  double umin2, umax2, vmin2, vmax2;
+  vector<double> param2_copy(param2.begin(), param2.end());  // Raster definition
+  // changes sequence of parameter points
+  defineRaster(param2_copy, nmb_div, raster2, umin2, umax2, vmin2, vmax2);
+
+  // Count number of empty cells
+  int nmb_zero2 = 0;
+  for (size_t kr=0; kr<raster2.size(); ++kr)
+    for (size_t kh=0; kh<raster2[kr].size(); ++kh)
+      if (raster2[kr][kh] == 0)
+	++nmb_zero2;
+
+  if (nmb_zero2 >= nmb_zero)
+    return false;
+
+  umin = umin2;
+  umax = umax2;
+  vmin = vmin2;
+  vmax = vmax2;
+  return true;
+}
+
+//===========================================================================
+void RevEngRegion::getParExtent(double curr[2], int pdir, vector<vector<int> >& raster,
+				int& i1, int& i2)
+//===========================================================================
+{
+  int div = (pdir == 0) ? (int)raster.size() : (int)raster[0].size();
+  int j1 = (int)curr[pdir];
+  int pdir2 = 1 - pdir;
+  
+  for (i1=(int)curr[pdir2]; i1<div; ++i1)
+    {
+      int r1 = (pdir == 0) ? raster[i1][j1] : raster[j1][i1];
+      if (r1 > 0)
+	break;
+    }
+  if (i1 == div)
+    i1=(int)curr[pdir2];
+  for (; i1<div; ++i1)
+    {
+      int r1 = (pdir == 0) ? raster[i1][j1] : raster[j1][i1];
+      if (r1 == 0)
+	break;
+    }
+  for (i2=(int)curr[pdir2]; i2>=0; --i2)
+    {
+      int r1 = (pdir == 0) ? raster[i2][j1] : raster[j1][i2];
+      if (r1 > 0)
+	break;
+    }
+  if (i2 < 0)
+    i2=(int)curr[pdir2];
+  for (; i2>=0; --i2)
+    {
+      int r1 = (pdir == 0) ? raster[i2][j1] : raster[j1][i2];
+      if (r1 == 0)
+      break;
+    }
+  }
+
+
+//===========================================================================
+void RevEngRegion::defineRaster(vector<double>& param, int nmb_div,
+				vector<vector<int> >& raster, double& umin,
+				double& umax, double& vmin, double& vmax)
+//===========================================================================
+{
+  umin = std::numeric_limits<double>::max();
+  umax = std::numeric_limits<double>::lowest();
+  vmin = std::numeric_limits<double>::max();
+  vmax = std::numeric_limits<double>::lowest();
+  for (size_t kr=0; kr<param.size(); kr+=2)
+    {
+      umin = std::min(umin, param[kr]);
+      umax = std::max(umax, param[kr]);
+      vmin = std::min(vmin, param[kr+1]);
+      vmax = std::max(vmax, param[kr+1]);
+    }
+  
+  int nm = nmb_div*nmb_div;
+  double dom = (umax-umin)*(vmax-vmin);
+  double c1 = std::pow((double)nm/dom, 1.0/2.0);
+  int div1, div2;
+  div1 = (int)(c1*(umax-umin));
+  ++div1;
+  div2 = (int)(c1*(vmax-vmin));
+  ++div2;
+  double udel = (umax - umin)/(double)(div1);
+  double vdel = (vmax - vmin)/(double)(div2);
+
+#ifdef DEBUG_REPAR
+  std::ofstream of("division_lines.g2");
+  of << "410 1 0 4 255 0 0 255" << std::endl;
+  of << div1+div2+2 << std::endl;
+  for (int ki=0; ki<=div1; ++ki)
+    {
+      Point p1(umin+ki*udel, vmin, 0.0);
+      Point p2(umin+ki*udel, vmax, 0.0);
+      of << p1 << " " << p2 << std::endl;
+    }
+  for (int ki=0; ki<=div2; ++ki)
+    {
+      Point p1(umin, vmin+ki*vdel, 0.0);
+      Point p2(umax, vmin+ki*vdel, 0.0);
+      of << p1 << " " << p2 << std::endl;
+    }
+#endif
+  
+  raster.resize(div2);
+  for (size_t kr=0; kr<raster.size(); ++kr)
+    {
+      raster[kr].resize(div1, 0);
+    }
+
+  int nmb_pts = (int)param.size()/2;
+  qsort(&param[0], nmb_pts, 2*sizeof(double), compare_v_par);
+  int pp0, pp1;
+  int ka, kb;
+  double upar, vpar;
+  for (vpar=vmin+vdel, pp0=0, kb=0; kb<div2; ++kb, vpar+=vdel)
+    {
+      for (pp1=pp0; pp1<2*nmb_pts && param[pp1+1]<=vpar; pp1+=2);
+      qsort(&param[pp0], (pp1-pp0)/2, 2*sizeof(double), compare_u_par);
+
+      int pp2, pp3;
+      for (upar=umin+udel, pp2=pp0, ka=0; ka<div1; ++ka, upar+=udel)
+	{
+	  for (pp3=pp2; pp3<pp1 && param[pp3]<=upar; pp3+=2);
+	  raster[kb][ka] = (pp3-pp2)/2;
+	  pp2 = pp3;
+	}
+      pp0 = pp1;
+    }
+}
+
+//===========================================================================
+void RevEngRegion::extendInCorner(vector<double>& data, vector<double>& param,
+				  double umin, double umax, double vmin, double vmax)
+//===========================================================================
+{
+  Point corner[4];
+  corner[0] = Point(umin, vmin);
+  corner[1] = Point(umax, vmin);
+  corner[2] = Point(umin, vmax);
+  corner[3] = Point(umax, vmax);
+  double cdist[4];
+  cdist[0] = cdist[1] = cdist[2] = cdist[3] = std::numeric_limits<double>::max();
+  for (size_t kr=0; kr<param.size(); kr+=2)
+    {
+      Point currpar(param[kr], param[kr+1]);
+      for (int ka=0; ka<4; ++ka)
+	{
+	  double dd = corner[ka].dist(currpar);
+	  cdist[ka] = std::min(cdist[ka], dd);
+	}
+    }
+
+  int div = 15;
+  double minc = std::min((umax-umin)/(double)div, (vmax-vmin)/(double)div);
+  vector<double> corner_data;
+  vector<double> corner_par;
+  int min_nmb = 20;
+  for (int ka=0; ka<4; ++ka)
+    {
+      if (cdist[ka] > minc)
+      {
+	// Estimate corner point
+	// Collect points in the vicinity of the corner
+	vector<double> data2;
+	vector<double> param2;
+	double umin2 = std::numeric_limits<double>::max();
+	double umax2 = std::numeric_limits<double>::lowest();
+	double vmin2 = std::numeric_limits<double>::max();
+	double vmax2 = std::numeric_limits<double>::lowest();
+	double lim = 1.1*cdist[ka];  //2.0
+	for (size_t kr=0; kr<param.size()/2; ++kr)
+	  {
+	    Point currpar(param[2*kr], param[2*kr+1]);
+	    if (currpar.dist(corner[ka]) < lim)
+	      {
+		data2.insert(data2.end(), data.begin()+3*kr, data.begin()+3*(kr+1));
+		param2.insert(param2.end(), param.begin()+2*kr, param.begin()+2*(kr+1));
+		umin2 = std::min(umin2, param[2*kr]);
+		umax2 = std::max(umax2, param[2*kr]);
+		vmin2 = std::min(vmin2, param[2*kr+1]);
+		vmax2 = std::max(vmax2, param[2*kr+1]);
+	      }
+	  }
+
+	while ((int)data2.size()/3 < min_nmb)
+	  {
+	    data2.clear();
+	    param2.clear();
+	    lim += 0.5*cdist[ka];
+	    for (size_t kr=0; kr<param.size()/2; ++kr)
+	      {
+		Point currpar(param[2*kr], param[2*kr+1]);
+		if (currpar.dist(corner[ka]) < lim)
+		  {
+		    data2.insert(data2.end(), data.begin()+3*kr, data.begin()+3*(kr+1));
+		    param2.insert(param2.end(), param.begin()+2*kr, param.begin()+2*(kr+1));
+		    umin2 = std::min(umin2, param[2*kr]);
+		    umax2 = std::max(umax2, param[2*kr]);
+		    vmin2 = std::min(vmin2, param[2*kr+1]);
+		    vmax2 = std::max(vmax2, param[2*kr+1]);
+		  }
+	      }
+	  }
+
+	// Approximate sub cloud with a planar surface
+	umin2 = std::min(umin2, corner[ka][0]);
+	umax2 = std::max(umax2, corner[ka][0]);
+	vmin2 = std::min(vmin2, corner[ka][1]);
+	vmax2 = std::max(vmax2, corner[ka][1]);
+	umin2 -= 0.1*(umax2 - umin2);
+	umax2 += 0.1*(umax2 - umin2);
+	vmin2 -= 0.1*(vmax2 - vmin2);
+	vmax2 += 0.1*(vmax2 - vmin2);
+
+	shared_ptr<SplineSurface> plane =
+	  RevEngUtils::surfApprox(data2, 3, param2, 2, 2, 2, 2, umin, umax,
+				  vmin, vmax);
+	Point pos = plane->ParamSurface::point(corner[ka][0], corner[ka][1]);
+	corner_data.insert(corner_data.end(), pos.begin(), pos.end());
+	corner_par.insert(corner_par.end(), corner[ka].begin(), corner[ka].end());
+      }
+    }
+
+  if (corner_data.size() > 0)
+    {
+      data.insert(data.end(), corner_data.begin(), corner_data.end());
+      param.insert(param.end(), corner_par.begin(), corner_par.end());
+
+#ifdef DEBUG_REPAR
+      std::ofstream of("added_corners.g2");
+      for (size_t ki=0; ki<corner_data.size()/3; ++ki)
+	{
+	  of << "400 1 0 4 255 0 0 255" << std::endl;
+	  of << "1" << std::endl;
+	  for (int ka=0; ka<3; ++ka)
+	    of << corner_data[3*ki+ka] << " ";
+	  of << std::endl;
+	}
+#endif
+    }
+}
+
+
+//===========================================================================
+bool RevEngRegion::parameterizeOnSurf(shared_ptr<ParamSurface> surf, 
+				      vector<double>& data, vector<double>& param,
+				      int& inner1, int& inner2, bool& close1, bool& close2)
+//===========================================================================
+{
+  return parameterizeOnSurf(group_points_, surf, data, param, inner1, inner2,
+			    close1, close2);
+}
+
+//===========================================================================
+bool RevEngRegion::parameterizeOnSurf(vector<RevEngPoint*>& points,
+				      shared_ptr<ParamSurface> surf, 
+				      vector<double>& data, vector<double>& param,
+				      int& inner1, int& inner2, bool& close1, bool& close2)
+//===========================================================================
+{
+  ClassType classtype = surf->instanceType();
+  if (classtype == Class_Cone || classtype == Class_Torus)
+    {
+      // Check angle. An almost plane cone is not appropriate for
+      // parametrization
+      shared_ptr<ParamSurface> sf = surf;
+      shared_ptr<BoundedSurface> bdsf =
+	dynamic_pointer_cast<BoundedSurface,ParamSurface>(sf);
+      if (bdsf.get())
+	sf = bdsf->underlyingSurface();
+      if (classtype == Class_Cone)
+	{
+	  shared_ptr<Cone> cone =
+	    dynamic_pointer_cast<Cone,ParamSurface>(sf);
+	  double angle = cone->getConeAngle();
+	  if (angle > 0.3*M_PI)
+	    return false;
+	}
+      else  if (classtype == Class_Torus)
+	{
+	  shared_ptr<Torus> torus =
+	    dynamic_pointer_cast<Torus,ParamSurface>(sf);
+	  double rad1 = torus->getMajorRadius();
+	  double rad2 = torus->getMinorRadius();
+	  if (rad2 > 0.9*rad1)
+	    return false;
+	}
+    }
+  
+  bool OK = RevEngUtils::parameterizeOnPrimary(points, surf, data, param,
+					       inner1, inner2, close1, close2);
+  return OK;
+}
+
+
+//===========================================================================
+void RevEngRegion::analyseCylinderProperties(Point avvec, double angtol,
+					  vector<RevEngPoint*>& in,
+					  vector<RevEngPoint*> out)
+//===========================================================================
+{
+  vector<double> midang(group_points_.size());
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point curr = group_points_[ki]->minCurvatureVec();
+      double ang = curr.angle(avvec);
+      ang = std::min(ang, M_PI-ang);
+      midang[ki] = ang;
+      if (ang <= angtol)
+	in.push_back(group_points_[ki]);
+      else
+	out.push_back(group_points_[ki]);
+    }
+
+  std::sort(midang.begin(), midang.end());
+  int stop_break = 1;
+}
+
+
+//===========================================================================
+bool RevEngRegion::sortByAxis(vector<Point>& axis, double tol,
+			      double axisang, double planeang,
+			      vector<vector<RevEngPoint*> >& groups1,
+			      vector<vector<RevEngPoint*> >& groups2,
+			      vector<RevEngPoint*>& remaining)
+//===========================================================================
+{
+  double pihalf = 0.5*M_PI;
+  groups1.resize(2*axis.size());
+  groups2.resize(axis.size());
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      Point normal = group_points_[kr]->getLocFuncNormal();
+      Point normal2 = group_points_[kr]->getTriangNormal();
+      int min_ix1 = -1, min_ix2 = -1;
+      double min_ang1 = pihalf, min_ang2 = pihalf;
+      for (int ka=0; ka<(int)axis.size(); ++ka)
+	{
+	  double ang = axis[ka].angle(normal);
+	  double ang2 = axis[ka].angle(normal2);
+	  ang = std::min(fabs(pihalf-ang), fabs(pihalf-ang2));
+	  if (ang < min_ang2)
+	    {
+	      min_ang2 = ang;
+	      min_ix2 = ka;
+	    }
+	}
+      for (int ka=0; ka<(int)axis.size(); ++ka)
+	{
+	  double ang = axis[ka].angle(normal);
+	  double ang2 = axis[ka].angle(normal2);
+	  if (std::min(ang,ang2) < min_ang1)
+	    {
+	      min_ang1 = std::min(ang,ang2);
+	      min_ix1 = 2*ka+1;
+	    }
+	  else if (std::min(M_PI-ang,M_PI-ang2) < min_ang1)
+	    {
+	      min_ang1 = std::min(M_PI-ang,M_PI-ang2);
+	      min_ix1 = 2*ka;
+	    }
+	}
+      if (min_ang1 > planeang && min_ang2 < min_ang1 &&
+	  min_ang2 < axisang && min_ix2 >= 0 )
+	groups2[min_ix2].push_back(group_points_[kr]);
+      else if (min_ang1 < axisang && min_ix1 >= 0)
+	groups1[min_ix1].push_back(group_points_[kr]);
+      else
+	remaining.push_back(group_points_[kr]);
+    }
+      
+#ifdef DEBUG_SEGMENT
+  std::ofstream of1("axis_groups1.g2");
+  for (size_t ki=0; ki<groups1.size(); ++ki)
+    {
+      if (groups1[ki].size() > 0)
+	{
+	  of1 << "400 1 0 4 255 0 0 255" << std::endl;
+	  of1 <<  groups1[ki].size() << std::endl;
+	  for (int ka=0; ka<(int)groups1[ki].size(); ++ka)
+	    of1 << groups1[ki][ka]->getPoint() << std::endl;
+	}
+    }
+  std::ofstream of2("axis_groups2.g2");
+  for (size_t ki=0; ki<groups2.size(); ++ki)
+    {
+      if (groups2[ki].size() > 0)
+	{
+	  of2 << "400 1 0 4 255 0 0 255" << std::endl;
+	  of2 <<  groups2[ki].size() << std::endl;
+	  for (int ka=0; ka<(int)groups2[ki].size(); ++ka)
+	    of2 << groups2[ki][ka]->getPoint() << std::endl;
+	}
+    }
+  std::ofstream of3("remaining.g2");
+  if (remaining.size() > 0)
+    {
+      of3 << "400 1 0 4 0 255 0 255" << std::endl;
+      of3 <<  remaining.size() << std::endl;
+      for (int ka=0; ka<(int)remaining.size(); ++ka)
+	of3 << remaining[ka]->getPoint() << std::endl;
+    }
+#endif
+  int num_groups = 0;
+  for (size_t ki=0; ki<groups1.size(); ++ki)
+    if (groups1.size() > 0)
+      num_groups++;
+  for (size_t ki=0; ki<groups2.size(); ++ki)
+    if (groups2.size() > 0)
+      num_groups++;
+  if (remaining.size() > 0)
+    num_groups++;
+    
+  return (num_groups > 1);
+}
+
+//===========================================================================
+void RevEngRegion::removeLowAccuracyPoints(int min_pt_reg, double tol, double angtol,
+					    vector<vector<RevEngPoint*> >& added_groups)
+//===========================================================================
+{
+  shared_ptr<ParamSurface> surf= getSurface(0)->surface();
+  int code;
+  ClassType classtype = getSurface(0)->instanceType(code);
+  bool cyllike = (classtype == Class_Cylinder || classtype == Class_Cone);
+  double angtol2 = (surfflag_ == PROBABLE_HELIX) ? 0.5*M_PI : 0.25*M_PI;
+  
+  vector<vector<RevEngPoint*> > out_groups;
+  vector<RevEngPoint*> remain;
+  vector<pair<double,double> > distang;
+  getDistAndAng(distang);
+  identifyOutPoints(distang, tol, angtol, angtol2, out_groups,
+		    remain);
+#ifdef DEBUG_AXIS
+  std::ofstream of1("dist_separated.g2");
+  of1 << "400 1 0 4 255 0 0 255" << std::endl;
+  of1 << remain.size() << std::endl;
+  for (size_t kr=0; kr<remain.size(); ++kr)
+    of1 << remain[kr]->getPoint() << std::endl;
+  for (size_t kj=0; kj<out_groups.size(); ++kj)
+    {
+      of1 << "400 1 0 4 0 255 0 255" << std::endl;
+      of1 << out_groups[kj].size() << std::endl;
+      for (size_t kr=0; kr<out_groups[kj].size(); ++kr)
+	of1 << out_groups[kj][kr]->getPoint() << std::endl;
+    }
+#endif
+
+  double maxdist, avdist;
+  int num_in, num2_in;
+  //int surf_flag2 = NOT_SET;
+  if (remain.size() < group_points_.size())
+    {
+      maxdist = avdist = 0.0;
+      num_in = num2_in = 0;
+      double frac = 1.0/(double)remain.size();
+      for (size_t kj=0; kj<remain.size(); ++kj)
+	{
+	  double dist, ang;
+	  remain[kj]->getSurfaceDist(dist, ang);
+	  maxdist = std::max(maxdist, dist);
+	  avdist += frac*dist;
+	  if (dist <= tol)
+	    {
+	      num2_in++;
+	      if (ang <= angtol)
+		num_in++;
+	    }
+	}
+      int surf_flag = defineSfFlag((int)remain.size(), 0, tol,
+				   num_in, num2_in, avdist, cyllike);
+      if (surf_flag <= surfflag_ && avdist < avdist_)
+	{
+	  // Extract points
+	  std::swap(group_points_, remain);
+	  added_groups.insert(added_groups.end(), out_groups.begin(),
+			      out_groups.end());
+
+	  updateInfo(tol, angtol);
+	  setSurfaceFlag(surf_flag);
+	  updateRegionAdjacency();
+	  
+	  int stop_break = 1;
+	}
+	  
+    }
+  
+}
+
+//===========================================================================
+void RevEngRegion::setPlaneParam(int min_pt_reg, Point mainaxis[3],
+				 double tol, double angtol)
+//===========================================================================
+{
+  if (!hasSurface())
+    return;
+  shared_ptr<ParamSurface> surf_init = getSurface(0)->surface();
+  if (surf_init->instanceType() != Class_Plane)
+    return;
+
+  shared_ptr<Plane> plane = dynamic_pointer_cast<Plane, ParamSurface>(surf_init);
+  int ix = -1;
+  double min_ang = M_PI;
+  double pihalf = 0.5*M_PI;
+  Point dir = plane->direction();
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = mainaxis[ka].angle(dir);
+      ang = fabs(pihalf-ang);
+      if (ang < min_ang)
+	{
+	  min_ang = ang;
+	  ix = ka;
+	}
+    }
+
+  Point dir2 = dir.cross(mainaxis[ix]);
+  dir2.normalize();
+  std::vector<double> rot_mat = GeometryTools::getRotationMatrix(dir2, min_ang);
+  Point rotated(3);
+  for (int ka=0; ka<3; ++ka)
+    for (int kb=0; kb<3; ++kb)
+      rotated[ka] += rot_mat[ka*3+kb]*dir[kb];
+
+  bool updated = updateSurfaceWithAxis(min_pt_reg, rotated, mainaxis, -1, tol,
+				       angtol, plane->location());
+  
+  shared_ptr<Plane> plane2(new Plane(plane->location(), plane->direction(), mainaxis[ix]));
+
+  // Reparameterize. The accuracy should stay the same
+  double maxd, avd;
+  int num_in, num2_in;
+  vector<pair<double, double> >  dist_ang;
+  vector<double> parvals;
+  vector<RevEngPoint*> inpt, outpt;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  plane, tol, maxd, avd, num_in, num2_in, inpt, outpt,
+			  parvals, dist_ang, angtol);
+
+  int sf_flag = defineSfFlag(0, tol, num_in, num2_in, avd, false);
+  for (size_t kh=0; kh<group_points_.size(); ++kh)
+    {
+      group_points_[kh]->setPar(Vector2D(parvals[2*kh], parvals[2*kh+1]));
+      group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+    }
+  HedgeSurface *hedge = getSurface(0);
+  hedge->replaceSurf(plane2);
+  setSurfaceFlag(sf_flag);
+  updateInfo(tol, angtol);
+  surf_adaption_ =  AXIS_ADAPTED;
+ 
+}
+
+//===========================================================================
+bool RevEngRegion::updateSurfaceWithAxis(int min_pt_reg, Point adj_axis, 
+					 Point mainaxis[3], int ix, double tol, 
+					 double angtol, Point pos)
+//===========================================================================
+{
+  bool updated = false;
+  if (!hasSurface())
+    return false;
+  vector<Point> curr_axis;
+  if (adj_axis.dimension() != 0)
+    curr_axis.push_back(adj_axis);
+  if (ix >=0 && ix < 3)
+    curr_axis.push_back(mainaxis[ix]);
+  if (curr_axis.size() == 0)
+    return false;
+
+  shared_ptr<ParamSurface> surf_init = getSurface(0)->surface();
+  int code;
+  ClassType classtype = getSurface(0)->instanceType(code);
+  bool cyllike = (classtype == Class_Cylinder || classtype == Class_Cone);
+  //double angtol2 = (surfflag_ == PROBABLE_HELIX) ? 0.5*M_PI : 0.25*M_PI;
+
+  // Ensure updated information
+  double maxdist1 = 0.0, avdist1 = 0.0;
+  int num_in1 = 0, num2_in1 = 0;
+  double frac = 1.0/(double)group_points_.size();
+  for (size_t kj=0; kj<group_points_.size(); ++kj)
+    {
+      double dist, ang;
+      group_points_[kj]->getSurfaceDist(dist, ang);
+      maxdist1 = std::max(maxdist1, dist);
+      avdist1 += frac*dist;
+      if (dist <= tol)
+	{
+	  num2_in1++;
+	  if (ang <= angtol)
+	    num_in1++;
+	}
+    }
+  int sf_flag1 = defineSfFlag(0, tol, num_in1, num2_in1,
+			      avdist1, cyllike);
+  
+  vector<double> maxdist(curr_axis.size());
+  vector<double> avdist(curr_axis.size());
+  vector<int> num_in(curr_axis.size());
+  vector<int> num2_in(curr_axis.size());
+  vector<vector<pair<double, double> > > dist_ang(curr_axis.size());
+  vector<vector<double> > parvals(curr_axis.size());
+  vector<int> sf_flag(curr_axis.size());
+  vector<shared_ptr<ParamSurface> > surf(curr_axis.size());
+   for (size_t ki=0; ki<curr_axis.size(); ++ki)
+    {
+      // Update surface
+      surf[ki] = surfaceWithAxis(group_points_, curr_axis[ki], pos,
+				 mainaxis);
+
+      // Check accuracy
+      vector<RevEngPoint*> inpt, outpt;
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      surf[ki], tol, maxdist[ki], avdist[ki], num_in[ki],
+			      num2_in[ki], inpt, outpt, parvals[ki],
+			      dist_ang[ki], angtol);
+
+      sf_flag[ki] = defineSfFlag(0, tol, num_in[ki], num2_in[ki],
+				 avdist[ki], cyllike);
+
+    }
+
+   // Prefer axis adapted
+   int numpt = numPoints();
+   double avfrac = 0.9;
+   double fracfrac = 0.9;
+   double tol2 = 0.5*tol;
+   int alt_ix = (curr_axis.size() > 1) ? 1 : 0;
+   if (alt_ix > 0)
+     {
+       double num_frac1 = (double)(num_in[0]+num2_in[0])/(double)numpt;
+       double num_frac2 = (double)(num_in[1]+num2_in[1])/(double)numpt;
+       if ((avdist[0] <= avfrac*avdist[1] || avdist[0] <= tol2) &&
+	   fracfrac*num_frac1 >= num_frac2)
+	 alt_ix = 0;
+     }
+
+   //double num_frac = (double)(num_in1+num2_in1)/(double)numpt;
+   //double num_frac_alt = (double)(num_in[alt_ix]+num2_in[alt_ix])/(double)numpt;
+   // if ((avfrac*avdist[alt_ix] <= avdist1 || avdist[alt_ix] <= tol2) &&
+   //     num_frac_alt >= fracfrac*num_frac)
+   if (sf_flag[alt_ix] < ACCURACY_POOR || sf_flag[alt_ix] <= sf_flag1)
+     {
+       // Replace
+       setBaseSf(surf_init, maxdist1, avdist1, num_in1, num2_in1);
+       for (size_t kh=0; kh<group_points_.size(); ++kh)
+	 {
+	   group_points_[kh]->setPar(Vector2D(parvals[alt_ix][2*kh],
+					      parvals[alt_ix][2*kh+1]));
+	   group_points_[kh]->setSurfaceDist(dist_ang[alt_ix][kh].first,
+					     dist_ang[alt_ix][kh].second);
+	 }
+       HedgeSurface *hedge = getSurface(0);
+       hedge->replaceSurf(surf[alt_ix]);
+       if (!surf[alt_ix]->isBounded())
+	 {
+	   double diag = bbox_.low().dist(bbox_.high());
+	   hedge->limitSurf(2*diag);
+	 }
+       setSurfaceFlag(sf_flag[alt_ix]);
+       updateInfo(tol, angtol);
+       for (size_t kj=0; kj<rev_edges_.size(); ++kj)
+	 {
+	   rev_edges_[kj]->replaceSurf(this, surf[alt_ix], tol);
+	   rev_edges_[kj]->increaseSurfChangeCount();
+	 }
+       surf_adaption_ = (adj_axis.dimension() != 0 && alt_ix == 0) ?
+	 ADJACENT_ADAPTED : AXIS_ADAPTED;
+       updated = true;
+     }
+   else
+     {
+       // Store alternative as base surface
+       setBaseSf(surf[alt_ix], maxdist[alt_ix], avdist[alt_ix],
+		 num_in[alt_ix], num2_in[alt_ix]);
+
+       setAccuracy(maxdist1, avdist1, num_in1, num2_in1);
+     }
+   
+   int stop_break = 1;
+
+   return updated;
+}
+
+//===========================================================================
+void RevEngRegion::updateSurfaceAndInfo(shared_ptr<ParamSurface> surf,
+					double tol, double angtol,
+					vector<double>& parvals,
+					vector<pair<double,double> >& dist_ang,
+					vector<RevEngEdge*>& nopar_edgs)
+//===========================================================================
+{
+  if (parvals.size() != 2*group_points_.size() ||
+      dist_ang.size() != group_points_.size())
+    THROW("RevEngRegion::updateSurfaceAndInfo: Inconsistent input");
+
+    shared_ptr<ParamSurface> surf_init = getSurface(0)->surface();
+
+    // Replace
+    setBaseSf(surf_init, maxdist_, avdist_, num_inside_, num_inside2_);
+    for (size_t kh=0; kh<group_points_.size(); ++kh)
+      {
+	group_points_[kh]->setPar(Vector2D(parvals[2*kh],
+					   parvals[2*kh+1]));
+	group_points_[kh]->setSurfaceDist(dist_ang[kh].first,
+					  dist_ang[kh].second);
+      }
+    HedgeSurface *hedge = getSurface(0);
+    hedge->replaceSurf(surf);
+    if (!surf->isBounded())
+      {
+	double diag = bbox_.low().dist(bbox_.high());
+	hedge->limitSurf(2*diag);
+      }
+    updateInfo(tol, angtol);
+    bool cyl_like = ((surf->instanceType() == Class_Cylinder ||
+		      surf->instanceType() == Class_Cone));
+    
+    int sf_flag = defineSfFlag((int)group_points_.size(), 0, tol, num_inside_,
+			       num_inside2_, avdist_, cyl_like);
+    setSurfaceFlag(sf_flag);
+    for (size_t kj=0; kj<rev_edges_.size(); ++kj)
+      {
+	rev_edges_[kj]->replaceSurf(this, surf, tol);
+	rev_edges_[kj]->increaseSurfChangeCount();
+	int missing = rev_edges_[kj]->missingParCrv();
+	if (missing > 0)
+	  nopar_edgs.push_back(rev_edges_[kj]);
+      }
+}
+
+//===========================================================================
+void RevEngRegion::rangeAlongAxis(const Point& pos, const Point& axis, 
+				  double& tmin, double& tmax)
+//===========================================================================
+{
+  tmin = std::numeric_limits<double>::max();
+  tmax = std::numeric_limits<double>::lowest();
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector3D xyz = group_points_[ki]->getPoint();
+      Point point(xyz[0], xyz[1], xyz[2]);
+      double tpar = (point - pos)*axis;
+      tmin = std::min(tmin, tpar);
+      tmax = std::max(tmax, tpar);
+    }
+}
+
+//===========================================================================
+void RevEngRegion::identifyOutPoints(vector<pair<double,double> >& distang,
+				     double tol, double angtol, double angtol2,
+				     vector<vector<RevEngPoint*> >& out_groups,
+				     vector<RevEngPoint*>& remaining)
+//===========================================================================
+{
+  // Identify points with poor accuracy
+  double fac = 2.0;
+  vector<RevEngPoint*> points_poor;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    if ((distang[ki].second > angtol && distang[ki].first > tol) ||
+	distang[ki].first > fac*tol || distang[ki].second > angtol2)
+      points_poor.push_back(group_points_[ki]);
+    else
+      remaining.push_back(group_points_[ki]);
+
+  // Release points in the inner of the group
+  if (points_poor.size() > 0)
+    {
+      vector<RevEngPoint*> inner;
+      connectedGroups(points_poor, out_groups, true, inner);
+      if (inner.size() > 0)
+	remaining.insert(remaining.end(), inner.begin(), inner.end());
+    }
+}
+
+//===========================================================================
+shared_ptr<ParamSurface> RevEngRegion::surfaceWithAxis(vector<RevEngPoint*>& points,
+						       Point axis, Point pos,
+						       Point mainaxis[3])
+//===========================================================================
+{
+  shared_ptr<ParamSurface> surf;
+  if (!hasSurface())
+    return surf;
+
+  shared_ptr<ParamSurface> init_surf = getSurface(0)->surface();
+  shared_ptr<ElementarySurface> elem =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(init_surf);
+  if (!elem.get())
+    return surf;
+
+  Point low = bbox_.low();
+  Point high = bbox_.high();
+  Point loc = elem->location();
+  if (hasBlendEdge())
+    {
+      // Maintain radius while updating surface
+      surf = init_surf;  // For the time being
+    }
+  else if (elem->instanceType() == Class_Plane)
+    {
+      Point init_norm = elem->direction();
+      surf = RevEngUtils::planeWithAxis(points, axis, loc, mainaxis);
+      if (axis*init_norm < 0.0)
+	surf->swapParameterDirection();
+    }
+  else if (elem->instanceType() == Class_Cylinder)
+    surf = RevEngUtils::cylinderWithAxis(points, axis, low, high,
+					 mainaxis);
+  else if (elem->instanceType() == Class_Torus)
+    surf = RevEngUtils::torusWithAxis(points, axis,
+				      pos.dimension() == 0 ? loc : pos,
+				      mainaxis);
+  else if (elem->instanceType() == Class_Sphere)
+    surf = RevEngUtils::sphereWithAxis(points, axis, mainaxis);
+  else if (elem->instanceType() == Class_Cone)
+    surf = RevEngUtils::coneWithAxis(points, axis, low, high,
+				     mainaxis);
+
+  return surf;
+}
+
+
+//===========================================================================
+void RevEngRegion::axisFromAdjacent(double angtol, vector<Point>& axis)
+//===========================================================================
+{
+  vector<pair<shared_ptr<ElementarySurface>, RevEngRegion*>  > adj_elem, adj_elem_base;
+  getAdjacentElemInfo(adj_elem, adj_elem_base);
+
+  for (size_t ki=0; ki<adj_elem.size(); ++ki)
+    {
+      if (adj_elem[ki].first->instanceType() == Class_Plane ||
+	  adj_elem[ki].first->instanceType() == Class_Cylinder ||
+	  adj_elem[ki].first->instanceType() == Class_Torus)
+	{
+	  Point dir = adj_elem[ki].first->direction();
+	  size_t kj;
+	  for (kj=0; kj<axis.size(); ++kj)
+	    {
+	      double ang = dir.angle(axis[kj]);
+	      ang = std::min(ang, M_PI-ang);
+	      if (ang < angtol)
+		break;
+	    }
+	  if (kj == axis.size())
+	    axis.push_back(dir);
+	}
+    }
+}
+
+//===========================================================================
+bool RevEngRegion::divideWithSegInfo(int seg_ix, int min_pt_reg, 
+				     vector<vector<RevEngPoint*> >& sep_groups,
+				     vector<RevEngPoint*>& single_pts)
+//===========================================================================
+{
+  if (seg_ix < 0 || seg_ix >= (int)seg_info_.size())
+    return false;
+
+  if (seg_info_[seg_ix]->type_ != 1)
+    return false;  // No strategy
+
+  // Extract points outside a belt around the specified axis
+  Point axis = seg_info_[seg_ix]->axis_;
+  Point pos = seg_info_[seg_ix]->loc_;
+  double lim1 = seg_info_[seg_ix]->min_dist_;
+  double lim2 = seg_info_[seg_ix]->max_dist_;
+  vector<RevEngPoint*> inside, outside;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      Vector3D xyz = group_points_[kr]->getPoint();
+      Point xyz2(xyz[0], xyz[1], xyz[2]);
+      Point onaxis = pos + ((xyz2-pos)*axis)*axis;
+      double dd = xyz2.dist(onaxis);
+      if (dd >= lim1 && dd <= lim2)
+	inside.push_back(group_points_[kr]);
+      else
+	outside.push_back(group_points_[kr]);
+    }
+
+  // Check for a reasonably balanced segmentation
+  int min_num = std::min(min_pt_reg, (int)group_points_.size()/10);
+  if ((int)inside.size() < min_num || (int)outside.size() < min_num)
+    return false;
+
+  // Ensure connected point groups
+  vector<std::vector<RevEngPoint*> > sep1, sep2;
+  std::vector<RevEngPoint*> dummy;
+  connectedGroups(inside, sep1, false, dummy);
+  connectedGroups(outside, sep2, false, dummy);
+  sep1.insert(sep1.end(), sep2.begin(), sep2.end());
+
+  // Identify largest group
+  int max_num = 0;
+  int ix = -1;
+  for (size_t ki=0; ki<sep1.size(); ++ki)
+    if ((int)sep1[ki].size() > max_num)
+      {
+	max_num = (int)sep1[ki].size();
+	ix = (int)ki;
+      }
+
+  if (ix >= 0)
+    {
+      group_points_.clear();
+      group_points_ = sep1[ix];
+      
+      // Update bounding box and principal curvature summary
+      updateInfo();
+      
+      for (size_t kj=0; kj<sep1.size(); ++kj)
+	{
+	  if ((int)kj == ix)
+	    continue;
+	  if (sep1[kj].size() == 1)
+	    {
+	      sep1[kj][0]->unsetRegion();
+	      single_pts.push_back(sep1[kj][0]);
+	    }
+	  else
+	    sep_groups.push_back(sep1[kj]);
+	}
+    }
+  return true;
+}
+
+
+//===========================================================================
+bool RevEngRegion::extractCylByAxis(Point mainaxis[3], int min_point,
+				    int min_pt_reg, double tol, 
+				    double angtol, int prefer_elementary,
+				    vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				    vector<shared_ptr<RevEngRegion> >& added_reg,
+				    vector<vector<RevEngPoint*> >& out_groups,
+				    vector<RevEngPoint*>& single_pts)
+//===========================================================================
+{
+  // Get axis from adjacent
+  vector<Point> axis;
+  axisFromAdjacent(angtol, axis);
+  
+  double pihalf = 0.5*M_PI;
+  double axisang = 0.1*M_PI;
+  vector<vector<RevEngPoint*> > axis_groups(3);
+  vector<RevEngPoint*> remaining;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      Point normal = group_points_[kr]->getLocFuncNormal();
+      int min_ix = -1;
+      double min_ang = pihalf;
+      for (int ka=0; ka<3; ++ka)
+	{
+	  double ang = mainaxis[ka].angle(normal);
+	  ang = fabs(pihalf-ang);
+	  if (ang < min_ang)
+	    {
+	      min_ang = ang;
+	      min_ix = ka;
+	    }
+	}
+      if (min_ang < axisang && min_ix >= 0)
+	axis_groups[min_ix].push_back(group_points_[kr]);
+      else
+	remaining.push_back(group_points_[kr]);
+    }
+      
+#ifdef DEBUG_SEGMENT
+  std::ofstream of("axis_groups.g2");
+  for (size_t ki=0; ki<axis_groups.size(); ++ki)
+    {
+      if (axis_groups[ki].size() > 0)
+	{
+	  of << "400 1 0 4 255 0 0 255" << std::endl;
+	  of <<  axis_groups[ki].size() << std::endl;
+	  for (int ka=0; ka<(int)axis_groups[ki].size(); ++ka)
+	    of << axis_groups[ki][ka]->getPoint() << std::endl;
+	}
+    }
+  if (remaining.size() > 0)
+    {
+      of << "400 1 0 4 0 255 0 255" << std::endl;
+      of <<  remaining.size() << std::endl;
+      for (int ka=0; ka<(int)remaining.size(); ++ka)
+	of << remaining[ka]->getPoint() << std::endl;
+    }
+#endif
+
+  size_t remain_size = remaining.size();
+  for (int ka=0; ka<(int)axis_groups.size(); ++ka)
+    if (axis_groups[ka].size() < remain_size)
+      {
+	remaining.insert(remaining.end(), axis_groups[ka].begin(), axis_groups[ka].end());
+	axis_groups[ka].clear();
+      }
+  
+  double remain_fac = 0.9;
+  if ((double)remaining.size()/(double)group_points_.size() > remain_fac)
+    return false;  // Not a feasible segmentation
+  for (int ka=0; ka<3; ++ka)
+    if (axis_groups[ka].size() == group_points_.size())
+      return false;
+
+  // Split into connected groups
+  for (size_t ki=0; ki<axis_groups.size(); ++ki)
+    {
+      if ((int)axis_groups[ki].size() < min_point)
+	{
+	  if (axis_groups[ki].size() > 0)
+	    remaining.insert(remaining.end(), axis_groups[ki].begin(),
+			     axis_groups[ki].end());
+	  continue;
+	}
+      
+      vector<vector<RevEngPoint*> > grouped;
+      std::vector<RevEngPoint*> dummy;
+      connectedGroups(axis_groups[ki], grouped, false, dummy);
+      
+      for (size_t kj=0; kj<grouped.size(); ++kj)
+	{
+	  if ((int)grouped[kj].size() < min_point)
+	    {
+	      remaining.insert(remaining.end(), grouped[kj].begin(),
+			       grouped[kj].end());
+	      continue;
+	    }
+
+	  // Try to fit a cylinder
+	  shared_ptr<RevEngRegion> tmp_reg(new RevEngRegion(classification_type_,
+							    edge_class_type_,
+							    grouped[kj]));
+	  tmp_reg->setRegionAdjacency();
+	  vector<HedgeSurface*> prevsfs;
+	  bool repeat = false;
+	  bool found = tmp_reg->extractCylinder(tol, min_point, min_pt_reg,
+						angtol,
+						prefer_elementary, hedgesfs,
+						prevsfs, out_groups, repeat);
+	  added_reg.push_back(tmp_reg);
+	}
+    }
+	  
+  vector<vector<RevEngPoint*> > grouped2;
+  std::vector<RevEngPoint*> dummy;
+  connectedGroups(remaining, grouped2, false, dummy);
+      
+  int max_num = 0;
+  int max_ix = -1;
+  for (size_t ki=0; ki<grouped2.size(); ++ki)
+    if ((int)grouped2[ki].size() > max_num)
+      {
+	max_num = (int)grouped2[ki].size();
+	max_ix = (int)ki;
+      }
+
+  if (max_ix >= 0)
+    {
+      std::swap(group_points_, grouped2[max_ix]);
+      std::swap(grouped2[max_ix], grouped2[grouped2.size()-1]);
+      grouped2.pop_back();
+      updateInfo(tol, angtol);
+    }
+  if (grouped2.size() > 0)
+    out_groups.insert(out_groups.end(), grouped2.begin(), grouped2.end());
+ 
+  return true;
+}
+
+//===========================================================================
+void RevEngRegion::computeFracNorm(double angtol, Point mainaxis[3],
+				   int nmb_axis[3], double& in_frac1,
+				   double& in_frac2)
+//===========================================================================
+{
+  double pihalf = 0.5*M_PI;
+  double axisang = 0.1*M_PI;
+  nmb_axis[0] = nmb_axis[1] = nmb_axis[2] = 0;
+  if (normalcone_.angle() <= angtol)
+    in_frac1 = 1.0;
+  else
+    {
+      int nmb_in = 0;
+      for (size_t kr=0; kr<group_points_.size(); ++kr)
+	{
+	  Point normal = group_points_[kr]->getLocFuncNormal();
+	  if (avnorm_.angle(normal) <= angtol)
+	    nmb_in++;
+	  for (int ka=0; ka<3; ++ka)
+	    {
+	      double ang = mainaxis[ka].angle(normal);
+	      if (fabs(pihalf-ang) < axisang)
+		nmb_axis[ka]++;
+	    }
+	      
+	}
+      in_frac1 = (double)nmb_in/(double)group_points_.size();
+    }
+  frac_norm_in_ = in_frac1;
+
+  if (normalcone2_.angle() <= angtol)
+    in_frac2 = 1.0;
+  else
+    {
+      int nmb_in = 0;
+      for (size_t kr=0; kr<group_points_.size(); ++kr)
+	{
+	  Point normal = group_points_[kr]->getTriangNormal();
+	  if (avnorm2_.angle(normal) <= angtol)
+	    nmb_in++;
+	  for (int ka=0; ka<3; ++ka)
+	    {
+	      double ang = mainaxis[ka].angle(normal);
+	      if (fabs(pihalf-ang) < axisang)
+		nmb_axis[ka]++;
+	    }
+	      
+	}
+      in_frac2 = (double)nmb_in/(double)group_points_.size();
+    }
+  frac_norm_in2_ = in_frac2;
+}
+
+//===========================================================================
+void
+RevEngRegion::initPlaneCyl(int min_point, int min_pt_reg, double tol, 
+			   double angtol, Point mainaxis[3],
+			   double zero_H, double zero_K,
+			   vector<shared_ptr<HedgeSurface> >& hedgesfs,
+			   vector<vector<RevEngPoint*> >& out_groups,
+			   vector<RevEngPoint*>& single_pts, bool& repeat)
+//===========================================================================
+{
+#ifdef DEBUG_SEGMENT
+  std::ofstream of1("init_sf_region.g2");
+  writeRegionPoints(of1);
+#endif
+
+  repeat = false;
+  
+  // Count fraction of normals closer to the centre than the tolerance
+  double angtol2 = 2.0*angtol;
+  //double dfrac = 0.75;
+  double in_frac, in_frac2;
+  int nmb_axis[3];
+  computeFracNorm(angtol2, mainaxis, nmb_axis, in_frac, in_frac2);
+
+  int min_pt_reg2 = 4*min_pt_reg/5; // Allow some slack
+
+  // Initial approximation with plane and cylinder
+  vector<shared_ptr<ElementarySurface> > elemsf(6);
+  vector<double> maxd(6), avd(6);
+  vector<int> num_in(6), num2_in(6);
+  vector<vector<double> > param(6);
+  vector<vector<pair<double,double> > > d_a(6);
+  vector<int> surfflag(6, NOT_SET);
+  
+  double in_lim = 0.9;
+  shared_ptr<Plane> plane = computePlane(group_points_, avnorm_, mainaxis);
+  vector<RevEngPoint*> inpt1, outpt1, inpt2, outpt2;
+  int num_ang_in1=0, num_ang_in2=0;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(), plane, 
+			  tol, maxd[4], avd[4], num_in[4], num2_in[4],
+			  inpt1, outpt1, param[4], d_a[4], angtol);
+  for (size_t kr=0; kr<d_a[4].size(); ++kr)
+    {
+      if (d_a[4][kr].second < angtol)
+	num_ang_in1++;
+    }
+  elemsf[4] = plane;
+  surfflag[4] = defineSfFlag(0, tol, num_in[4], num2_in[4], avd[4],
+			      false);
+
+  shared_ptr<Cylinder> cyl = computeCylinder(group_points_, tol);
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(), cyl, 
+			  tol, maxd[5], avd[5], num_in[5], num2_in[5],
+			  inpt2, outpt2, param[5], d_a[5], angtol);
+
+  for (size_t kr=0; kr<d_a[5].size(); ++kr)
+    {
+      if (d_a[5][kr].second < angtol)
+	num_ang_in2++;
+    }
+  elemsf[5] = cyl;
+  surfflag[5] = defineSfFlag(0, tol, num_in[5], num2_in[5], avd[5],
+			      true);
+
+  // Check with reduced point set
+  vector<vector<RevEngPoint*> > remaining(4);
+  vector<vector<vector<RevEngPoint*> > > extracted(6);  // Two last is empty
+    
+  int min_ang_in = (int)group_points_.size()/20;
+  int min_ang_in2 = (int)group_points_.size()/5;
+  double dfac = 3.0;
+  int nfac = 2;
+  if (accuracyOK(min_point, tol, num_in[4], avd[4]) == false &&
+      avd[4] < avd[5] && avd[4] < dfac*tol && num2_in[4] > num2_in[5] && 
+      num_ang_in1 > min_ang_in && num_ang_in1 > min_point)
+    {
+      // Search for a (reasonable) connected planar component in the point cloud
+      Point vec = plane->direction();
+      elemsf[0] = planarComponent(vec, min_point, min_pt_reg2,
+				  tol, angtol, mainaxis,
+				  remaining[0], extracted[0]);
+    }
+
+    if (accuracyOK(min_point, tol, num_in[5], avd[5]) == false &&
+      normalcone_.greaterThanPi() && avd[5] < avd[4] && avd[5] < dfac*tol &&
+      num2_in[5] >= nfac*num_in[5])
+    {
+      // Potential helical surface. Cleanup in points and register occurance
+      elemsf[1] = helicalComponent(cyl, tol, angtol, min_point, min_pt_reg2,
+				   avd[5], num2_in[5], d_a[5],
+				   remaining[1], extracted[1],
+				   maxd[1], avd[1], num_in[1], num2_in[1],
+				   param[1], d_a[1]);
+    }
+
+  vector<vector<RevEngPoint*> > out_gr2;
+  if ((in_frac >= in_lim && (!(MAH_ > 10.0*MAK_ && MAH_ > 0.1))) ||
+      accuracyOK(min_point, tol, num_in[4], avd[4]) ||
+      num_ang_in1 >= min_ang_in2)
+    {
+      vector<RevEngPoint*> remain1;
+      vector<vector<RevEngPoint*> > out_gr1;
+      identifyOutPoints(d_a[4], tol, angtol, angtol2, out_gr1, remain1);
+
+#ifdef DEBUG_SEGMENT
+      if (out_gr1.size() > 0)
+	{
+	  std::ofstream ofa("ang_points1.g2");
+	  ofa << "400 1 0 4 255 0 0 255" << std::endl;
+	  ofa << remain1.size() << std::endl;
+	  for (size_t kr=0; kr<remain1.size(); ++kr)
+	    ofa << remain1[kr]->getPoint() << std::endl;
+	  for (size_t kh=0; kh<out_gr1.size(); ++kh)
+	    {
+	      ofa << "400 1 0 4 50 50 155 255" << std::endl;
+	      ofa << out_gr1[kh].size() << std::endl;
+	      for (size_t kr=0; kr<out_gr1[kh].size(); ++kr)
+		ofa << out_gr1[kh][kr]->getPoint() << std::endl;
+	    }
+	}
+#endif
+
+      if ((int)remain1.size() > min_pt_reg2)
+	{
+	  vector<vector<RevEngPoint*> > connected;
+	  vector<RevEngPoint*> inner;
+	  connectedGroups(remain1, connected, false, inner);
+	  for (size_t kr=1; kr<connected.size(); ++kr)
+	    if (connected[kr].size() > connected[0].size())
+	      std::swap(connected[0], connected[kr]);
+	  std::swap(remain1, connected[0]);
+	  out_gr1.insert(out_gr1.end(), connected.begin()+1, connected.end());
+
+	  elemsf[2] = computePlane(remain1, avnorm_, mainaxis);
+	  remaining[2] = remain1;
+	  extracted[2] = out_gr1;
+	}
+    }
+  
+  if (MAH_ > 3*MAK_ || accuracyOK(min_point, tol, num_in[5], avd[5]) ||
+      num_ang_in2 >= min_ang_in2)
+    {
+      vector<RevEngPoint*> remain2;
+      identifyOutPoints(d_a[5], tol, angtol, angtol2, out_gr2, remain2);
+
+#ifdef DEBUG_SEGMENT
+      if (out_gr2.size() > 0)
+	{
+	  std::ofstream ofa("ang_points2.g2");
+	  ofa << "400 1 0 4 255 0 0 255" << std::endl;
+	  ofa << remain2.size() << std::endl;
+	  for (size_t kr=0; kr<remain2.size(); ++kr)
+	    ofa << remain2[kr]->getPoint() << std::endl;
+	  for (size_t kh=0; kh<out_gr2.size(); ++kh)
+	    {
+	      ofa << "400 1 0 4 50 50 155 255" << std::endl;
+	      ofa << out_gr2[kh].size() << std::endl;
+	      for (size_t kr=0; kr<out_gr2[kh].size(); ++kr)
+		ofa << out_gr2[kh][kr]->getPoint() << std::endl;
+	    }
+	}
+#endif
+
+       double rfac = 0.5;
+       //size_t remain2_size = remain2.size();
+       if ((double)remain2.size() < rfac*(double)group_points_.size())
+       {
+	 vector<RevEngPoint*> ang_pts;
+	 vector<RevEngPoint*> remain_ang;
+	 double dtol = 2.0*avd[5];
+	 identifyAngPoints(d_a[5], angtol, tol, dtol, ang_pts, remain_ang);
+      
+#ifdef DEBUG_SEGMENT
+	 std::ofstream ofa("ang_pts_cyl.g2");
+	 ofa << "400 1 0 4 155 50 50 255" << std::endl;
+	 ofa << ang_pts.size() << std::endl;
+	 for (size_t kr=0; kr<ang_pts.size(); ++kr)
+	   ofa << ang_pts[kr]->getPoint() << std::endl;
+#endif
+	 if (remain_ang.size() > remain2.size())
+	   {
+	     std::swap(remain_ang, remain2);
+	     vector<vector<RevEngPoint*> > connected2;
+	     vector<RevEngPoint*> dummy_inner;
+	     connectedGroups(ang_pts, connected2, false, dummy_inner);
+	     std::swap(out_gr2, connected2);
+	   }
+       }
+	 
+       if ((int)remain2.size() > min_pt_reg2)
+	 {
+	   shared_ptr<Cylinder> cyl2 = computeCylinder(remain2, tol);
+ 
+	   double len = bbox_.low().dist(bbox_.high());
+	   Point axis = cyl2->direction();
+	   Point Cx = cyl2->direction2();
+	   Point loc = cyl2->location();
+	   shared_ptr<Cone> cone =
+	     RevEngUtils::coneWithAxis(remain2, axis, Cx, loc, len);
+
+	   // Check accuracy with respect to reduced point cloud
+	   // For cylinder
+	   double maxdist3, avdist3;
+	   int num_inside3, num2_inside3=0;
+	   vector<RevEngPoint*> inpt3, outpt3;
+	   vector<double> parvals3;
+	   vector<pair<double, double> > dist_ang3;
+	   RevEngUtils::distToSurf(remain2.begin(), remain2.end(), cyl2, 
+				   tol, maxdist3, avdist3, num_inside3,
+				   num2_inside3, inpt3, outpt3, parvals3,
+				   dist_ang3, angtol);
+	   int sf_flag3 = defineSfFlag((int)remain2.size(), 0, tol, num_inside3,
+				       num2_inside3, avdist3, true);
+
+	   // For cone
+	   int sf_flag4 = NOT_SET;
+	   double maxdist4, avdist4;
+	   int num_inside4, num2_inside4=0;
+	   vector<RevEngPoint*> inpt4, outpt4;
+	   vector<double> parvals4;
+	   vector<pair<double, double> > dist_ang4;
+	   double cone_fac = 0.1*angtol;
+	   if (sf_flag3 >= ACCURACY_POOR ||
+	       fabs(cone->getConeAngle()) > cone_fac*angtol)
+	     {
+	       vector<RevEngPoint*> inpt4, outpt4;
+	       RevEngUtils::distToSurf(remain2.begin(), remain2.end(), cone, 
+				       tol, maxdist4, avdist4, num_inside4,
+				       num2_inside4, inpt4, outpt4, parvals4,
+				       dist_ang4, angtol);
+	       sf_flag4 = defineSfFlag((int)remain2.size(), 0, tol, num_inside4,
+				       num2_inside4, avdist4, true);
+	     }
+
+	   if (std::min(sf_flag3, sf_flag4) < ACCURACY_POOR)
+	     {
+	       remaining[3] = remain2;
+	       extracted[3] = out_gr2;
+
+	       if (sf_flag3 < sf_flag4 ||
+		   (sf_flag3 == sf_flag4 &&
+		    (avdist3 < avdist4 || num_inside3 > num_inside4)))
+		 {
+		   elemsf[3] = cyl2;
+		   maxd[3] = maxdist3;
+		   avd[3] = avdist3;
+		   num_in[3] = num_inside3;
+		   num2_in[3] = num2_inside3;
+		   param[3] = parvals3;
+		   d_a[3] = dist_ang3;
+		   surfflag[3] = sf_flag3;
+		 }
+	       else
+		 {
+		   elemsf[3] = cone;
+		   maxd[3] = maxdist4;
+		   avd[3] = avdist4;
+		   num_in[3] = num_inside4;
+		   num2_in[3] = num2_inside4;
+		   param[3] = parvals4;
+		   d_a[3] = dist_ang4;
+		   surfflag[3] = sf_flag4;
+		 }
+	     }
+	 }
+    }
+
+  // Compute accuracy for remaining candidates
+  for (size_t ki=0; ki<elemsf.size(); ++ki)
+    {
+      if (!elemsf[ki].get())
+	continue;  // Not a candidate
+      if (surfflag[ki] < NOT_SET)
+	continue;  // Already computed
+
+      if (param[ki].size() == 0)
+	{
+	   vector<RevEngPoint*> inpt3, outpt3;
+	   RevEngUtils::distToSurf(remaining[ki].begin(), remaining[ki].end(),
+				   elemsf[ki], tol, maxd[ki], avd[ki],
+				   num_in[ki], num2_in[ki], inpt3, outpt3,
+				   param[ki], d_a[ki], angtol);
+	}
+      bool cyllike = (elemsf[ki]->instanceType() == Class_Cylinder ||
+		      elemsf[ki]->instanceType() == Class_Cone);
+      surfflag[ki] = defineSfFlag((int)remaining[ki].size(), 0, tol,
+				  num_in[ki], num2_in[ki], avd[ki], cyllike);
+    }
+
+  // Select surface
+  int perm[6] = {4, 5, 2, 3, 0, 1};
+  size_t num_pts_in[6];
+  for (size_t kr=0; kr<4; ++kr)
+    num_pts_in[kr] = remaining[kr].size();
+  num_pts_in[4] = num_pts_in[5] = group_points_.size();
+  double size_fac = 0.9;
+  double avd_fac = 0.95;
+  
+  int ix = -1;
+  for (int ka=0; ka<6; ++ka)
+    {
+      if (surfflag[perm[ka]] >= ACCURACY_POOR)
+	continue;
+      if (ix < 0)
+	ix = ka;
+      else
+	{
+	  if (surfflag[perm[ka]] == surfflag[perm[ix]] ||
+	      (surfflag[perm[ka]] == ANGULAR_DEVIATION &&
+	       surfflag[perm[ix]] == PROBABLE_HELIX) ||
+	      (surfflag[perm[ix]] == ANGULAR_DEVIATION &&
+	       surfflag[perm[ka]] == PROBABLE_HELIX))
+	    {
+	      //int curr_num = std::max(num_pts_in[perm[ix]], num_pts_in[perm[ka]]);
+	      int curr_num = ((int)num_pts_in[perm[ix]] +
+			      (int)num_pts_in[perm[ka]])/2;
+	      double frac1 = (double)(num_in[perm[ix]] + num2_in[perm[ix]])/
+		(double)curr_num;
+	      double frac2 = (double)(num_in[perm[ka]] + num2_in[perm[ka]])/
+		(double)curr_num;
+
+	      bool prefer_first = false;
+	      if (perm[ix] % 2 == 0 && perm[ka] % 2 == 1)
+		{
+		  // Replace plane by rotational surface?
+		  double r = elemsf[perm[ka]]->radius(0.0, 0.0);
+		  double d = bbox_.low().dist(bbox_.high());
+		  double x2 = 4*r*r - d*d;
+		  double x = (x2 > 0.0) ? r - 0.5*sqrt(x2) : r;
+		  double rfac = 0.05;
+		  if (x < rfac*r)
+		    prefer_first = true;
+		}
+	      
+
+	      if (prefer_first)
+		;   // Keep current
+	      else if (avd[perm[ka]] <= tol && avd_fac*avd[perm[ix]] > tol)
+		ix = ka;
+	      else if (size_fac*(double)num_pts_in[perm[ka]] >
+		  (double)num_pts_in[perm[ix]])
+		ix = ka;
+	      else if (avd[perm[ka]] < avd_fac*avd[perm[ix]] && frac2 > frac1)
+		ix = ka;
+		  
+	    }
+	  else if (surfflag[perm[ka]] < surfflag[perm[ix]])
+	    ix = ka;
+	}
+    }
+
+  if (ix >= 0)
+    {
+      // Surface found
+      int ix2 = perm[ix];
+
+      // Pre check for connected group
+      vector<vector<RevEngPoint*> > grouped;
+      std::vector<RevEngPoint*> dummy;
+      connectedGroups((ix2<4) ? remaining[ix2] : group_points_, grouped,
+		      false, dummy);
+      int ix3 = 0;
+      for (size_t kr=1; kr<grouped.size(); ++kr)
+	if (grouped[kr].size() > grouped[ix3].size())
+	  ix3 = (int)kr;
+
+      if (grouped[ix3].size() < min_pt_reg2 || (!elemsf[ix2].get()))
+	ix = -1;
+    }
+     
+  if (ix >= 0)
+    {
+      // Surface found
+      int ix2 = perm[ix];
+
+      if (ix2 < 4)
+	std::swap(group_points_, remaining[ix2]);
+
+      for (size_t kh=0; kh<group_points_.size(); ++kh)
+	{
+	  group_points_[kh]->setPar(Vector2D(param[ix2][2*kh],
+					     param[ix2][2*kh+1]));
+	  group_points_[kh]->setSurfaceDist(d_a[ix2][kh].first,
+					    d_a[ix2][kh].second);
+	}
+      setAccuracy(maxd[ix2], avd[ix2], num_in[ix2], num2_in[ix2]);
+      shared_ptr<HedgeSurface> hedge(new HedgeSurface(elemsf[ix2], this));
+      setHedge(hedge.get());
+      hedgesfs.push_back(hedge);
+      setSurfaceFlag(surfflag[ix2]);
+      
+      // Ensure connected region
+      size_t num_extract = extracted[ix2].size();
+      for (size_t kr=0; kr<extracted[ix2].size(); ++kr)
+	for (size_t kh=0; kh<extracted[ix2][kr].size(); ++kh)
+	  extracted[ix2][kr][kh]->unsetRegion();
+	
+      vector<vector<RevEngPoint*> > separate_groups;
+      splitRegion(separate_groups);
+      if (separate_groups.size() > 0)
+	{
+	  extracted[ix2].insert(extracted[ix2].end(), separate_groups.begin(),
+				separate_groups.end());
+      
+	  for (size_t kr=num_extract; kr<extracted[ix2].size(); ++kr)
+	    for (size_t kh=0; kh<extracted[ix2][kr].size(); ++kh)
+	      extracted[ix2][kr][kh]->unsetRegion();
+      
+	  if (hasSurface())
+	    checkReplaceSurf(mainaxis, min_pt_reg, tol, angtol);
+	}
+	
+	for (size_t kr=0; kr<extracted[ix2].size(); )
+	  {
+	    if (extracted[ix2][kr].size() == 1)
+	      {
+		extracted[ix2][kr][0]->unsetRegion();
+		single_pts.push_back(extracted[ix2][kr][0]);
+		extracted[ix2].erase(extracted[ix2].begin()+kr);
+	      }
+	    else
+	      ++kr;
+	  }
+	out_groups = extracted[ix2];
+  
+#ifdef DEBUG_SEGMENT
+	std::ofstream ofb("res_group.g2");
+	writeRegionPoints(ofb);
+	writeSurface(ofb);
+#endif
+	int stop_break = 1;
+    }
+}
+
+
+
+
+//===========================================================================
+void RevEngRegion::splitRegion(vector<vector<RevEngPoint*> >& separate_groups)
+//===========================================================================
+{
+  vector<vector<RevEngPoint*> > connected;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      if (group_points_[ki]->visited())
+	continue;
+      vector<RevEngPoint*> curr_group;
+      group_points_[ki]->fetchConnected(this, (int)group_points_.size(), curr_group);
+      connected.push_back(curr_group);
+    }
+
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      group_points_[ki]->unsetVisited();
+    }
+
+#ifdef DEBUG_SEGMENT
+  std::ofstream of("curr_region_split2.g2");
+  for (size_t kj=0; kj<connected.size(); ++kj)
+    {
+      of << "400 1 0 0" << std::endl;
+      of << connected[kj].size() << std::endl;
+      for (size_t kr=0; kr<connected[kj].size(); ++kr)
+	of << connected[kj][kr]->getPoint() << std::endl;
+    }
+#endif
+  
+  if (connected.size() <= 1)
+    return;
+  
+  int max_num = 0;
+  int ix = -1;
+  for (size_t ki=0; ki<connected.size(); ++ki)
+    if ((int)connected[ki].size() > max_num)
+      {
+	max_num = (int)connected[ki].size();
+	ix = (int)ki;
+      }
+
+  if (ix >= 0)
+    {
+      group_points_.clear();
+      group_points_ = connected[ix];
+      
+      // Update bounding box and principal curvature summary
+      updateInfo();
+      
+      for (size_t kj=0; kj<connected.size(); ++kj)
+	{
+	  if ((int)kj == ix)
+	    continue;
+	  for (size_t kr=0; kr<connected[kj].size(); ++kr)
+	    connected[kj][kr]->unsetRegion();
+	  separate_groups.push_back(connected[kj]);
+	}
+    }
+}
+
+//===========================================================================
+shared_ptr<Plane>
+RevEngRegion::planarComponent(Point vec, int min_point, int min_pt_reg,
+			      double tol, double angtol, Point mainaxis[3],
+			      vector<RevEngPoint*>& remaining,
+			      vector<vector<RevEngPoint*> >& extracted)
+//===========================================================================
+{
+  shared_ptr<Plane> dummy_plane;
+  vector<vector<RevEngPoint*> > vec_pts(2);
+  int min_size = std::max(10, std::min(min_point, (int)group_points_.size()/20));
+  vector<RevEngPoint*> pts_out;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point normal = group_points_[ki]->getLocFuncNormal();
+      Point normal2 = group_points_[ki]->getTriangNormal();
+      double ang = vec.angle(normal);
+      double ang2 = vec.angle(normal2);
+      if (ang < angtol || ang2 < angtol)
+	vec_pts[0].push_back(group_points_[ki]);
+      else if (M_PI-ang < angtol || M_PI-ang < angtol)
+	vec_pts[1].push_back(group_points_[ki]);
+      else
+	pts_out.push_back(group_points_[ki]);
+    }
+
+  double eps = 1.0e-6;
+  double *seed = 0;
+  vector<vector<RevEngPoint*> > conn_groups;
+  for (int ka=0; ka<2; ++ka)
+    {
+      if ((int)vec_pts[ka].size() < min_size)
+	continue;
+      
+      // Check if the group can be represented by a plane
+      shared_ptr<Plane> plane = computePlane(vec_pts[ka], vec, mainaxis);
+      
+      vector<RevEngPoint*> inpt, outpt;
+      vector<pair<double, double> > dist_ang;
+      vector<double> parvals;
+      double maxdist, avdist;
+      int num_inside, num2_inside;
+      RevEngUtils::distToSurf(vec_pts[ka].begin(), vec_pts[ka].end(), plane, 
+			      tol, maxdist, avdist, num_inside, num2_inside,
+			      inpt, outpt, parvals, dist_ang, angtol);
+      int sf_flag0 = defineSfFlag((int)vec_pts[ka].size(), 0, tol, num2_inside,
+				 num_inside, avdist, false);
+      if (sf_flag0 < ACCURACY_POOR)
+	{
+	  // Move points from out group if feasible
+	  for (int kb=(int)pts_out.size()-1; kb>=0; --kb)
+	    {
+	      Vector3D xyz = pts_out[kb]->getPoint();
+	      Point pos(xyz[0], xyz[1], xyz[2]);
+	      double upar, vpar, dist;
+	      Point close;
+	      plane->closestPoint(pos, upar, vpar, close, dist, eps, 0, seed);
+	      if (dist <= tol)
+		{
+		  vec_pts[ka].push_back(pts_out[kb]);
+		  pts_out.erase(pts_out.begin()+kb);
+		}
+	    }
+	      
+	  std::vector<RevEngPoint*> dummy;
+	  connectedGroups(vec_pts[ka], conn_groups, false, dummy);
+	}
+    }
+  
+  // Identify largest group
+  if (conn_groups.size() == 0)
+    return dummy_plane;  // No large planar component is found
+  
+  int max_group = 0;
+  int ix = -1;
+  for (size_t ki=0; ki<conn_groups.size(); ++ki)
+    {
+      if ((int)conn_groups[ki].size() > max_group)
+	{
+	  max_group = (int)conn_groups[ki].size();
+	  ix = (int)ki;
+	}
+    }
+
+  if (max_group < min_size)
+    return dummy_plane;   // No large planar component is found
+
+   // Update plane
+  shared_ptr<Plane> plane2 = computePlane(conn_groups[ix], vec, mainaxis);
+	  
+#ifdef DEBUG_PLANAR
+  std::ofstream of("planar_component.g2");
+  writeRegionPoints(of);
+  shared_ptr<Plane> plane3(plane2->clone());
+  double len = bbox_.low().dist(bbox_.high());
+  plane3->setParameterBounds(-len, -len, len, len);
+  plane3->writeStandardHeader(of);
+  plane3->write(of);
+#endif
+
+  // Check angular behaviour of points
+  vector<RevEngPoint*> inpt2, outpt2;
+  vector<pair<double, double> > dist_ang2;
+  vector<double> parvals2;
+  double maxdist2, avdist2;
+  int num_inside2, num2_inside2;
+  RevEngUtils::distToSurf(conn_groups[ix].begin(), conn_groups[ix].end(),
+			  plane2, tol, maxdist2, avdist2, num_inside2, num2_inside2,
+			  inpt2, outpt2, parvals2, dist_ang2, angtol);
+  int sf_flag = defineSfFlag((int)conn_groups[ix].size(), 0, tol, num_inside2,
+			     num2_inside2, avdist2, false);
+  if (sf_flag >= ACCURACY_POOR)
+    return dummy_plane;
+  
+  vector<RevEngPoint*> ang_out;
+  if (sf_flag > ACCURACY_OK)
+    {
+      // Check if any points should be removed
+      size_t num_in = conn_groups[ix].size();
+      double dtol = std::min(0.5*tol, 1.5*avdist2);
+      for (size_t kr=0; kr<conn_groups[ix].size(); ++kr)
+	if (dist_ang2[kr].second > 2.0*angtol && dist_ang2[kr].first > dtol)
+	  {
+	    std::swap(conn_groups[ix][kr], conn_groups[ix][num_in-1]);
+	    --num_in;
+	  }
+      
+      if (num_in < conn_groups[ix].size())
+	{
+	  ang_out.insert(ang_out.end(), conn_groups[ix].begin()+num_in,
+			 conn_groups[ix].end());
+	  conn_groups[ix].erase(conn_groups[ix].begin()+num_in,
+				conn_groups[ix].end());
+#ifdef DEBUG_PLANAR
+	  std::ofstream ofa("ang_points_pc.g2");
+	  ofa << "400 1 0 4 50 50 155 255" << std::endl;
+	  ofa << ang_out.size() << std::endl;
+	  for (size_t kr=0; kr<ang_out.size(); ++kr)
+	    ofa << ang_out[kr]->getPoint() << std::endl;
+#endif
+	}
+    }
+
+  if ((int)conn_groups[ix].size() < max_group)
+    return dummy_plane;
+
+  // A result is reached. 
+  vector<vector<RevEngPoint*> > connected;
+  vector<RevEngPoint*> dummy_inner;
+  connectedGroups(conn_groups[ix], connected, false, dummy_inner);
+  if (connected.size() > 1)
+    {
+      for (size_t kr=0; kr<connected.size(); ++kr)
+	for (size_t kh=kr+1; kh<connected.size(); ++kh)
+	  if (connected[kh].size() > connected[kr].size())
+	    std::swap(connected[kh], connected[kr]);
+      conn_groups[ix] = connected[0];
+      conn_groups.insert(conn_groups.end(), connected.begin()+1,
+			 connected.end());
+    }
+  
+  remaining = conn_groups[ix];
+  for (size_t kr=0; kr<conn_groups.size(); ++kr)
+    {
+      if ((int)kr == ix)
+	continue;
+      extracted.push_back(conn_groups[kr]);
+    }
+  if (ang_out.size() > 0)
+    extracted.push_back(ang_out);
+
+  vector<vector<RevEngPoint*> > connected2;
+  connectedGroups(pts_out, connected2, false, dummy_inner);
+  extracted.insert(extracted.end(), connected2.begin(), connected2.end());
+  
+  return plane2;
+ }
+
+
+//===========================================================================
+bool RevEngRegion::planarComponent(Point vec, int min_point, int min_pt_reg,
+				   double tol, double angtol, Point mainaxis[3],
+				   vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				   vector<vector<RevEngPoint*> >& out_groups,
+				   vector<RevEngPoint*>& single_pts,
+				   bool create_surface)
+//===========================================================================
+{
+  vector<vector<RevEngPoint*> > vec_pts(2);
+  vector<RevEngPoint*> remaining;
+  int min_size = std::max(10, std::min(min_point, (int)group_points_.size()/20));
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Point normal = group_points_[ki]->getLocFuncNormal();
+      Point normal2 = group_points_[ki]->getTriangNormal();
+      double ang = vec.angle(normal);
+      double ang2 = vec.angle(normal2);
+      if (ang < angtol || ang2 < angtol)
+	vec_pts[0].push_back(group_points_[ki]);
+      else if (M_PI-ang < angtol || M_PI-ang < angtol)
+	vec_pts[1].push_back(group_points_[ki]);
+      else
+	remaining.push_back(group_points_[ki]);
+    }
+
+  double eps = 1.0e-6;
+  double *seed = 0;
+  vector<vector<RevEngPoint*> > conn_groups;
+  for (int ka=0; ka<2; ++ka)
+    {
+      if ((int)vec_pts[ka].size() < min_size)
+	continue;
+
+	  // Check if the group can be represented by a plane
+	  shared_ptr<Plane> plane = computePlane(vec_pts[ka], vec, mainaxis);
+	  
+	  vector<RevEngPoint*> inpt, outpt;
+	  vector<pair<double, double> > dist_ang;
+	  vector<double> parvals;
+	  double maxdist, avdist;
+	  int num_inside, num2_inside;
+	  RevEngUtils::distToSurf(vec_pts[ka].begin(), vec_pts[ka].end(),
+				  plane, tol, maxdist, avdist, num_inside, num2_inside,
+				  inpt, outpt, parvals, dist_ang, -1);
+	  if (num_inside > min_point && num_inside > (int)vec_pts[ka].size()/2 &&
+	      avdist <= tol)
+	    {
+	      // Move points from remaining group if feasible
+	      for (size_t kr=0; kr<remaining.size(); ++kr)
+		{
+		  Vector3D xyz = remaining[kr]->getPoint();
+		  Point pos(xyz[0], xyz[1], xyz[2]);
+		  double upar, vpar, dist;
+		  Point close;
+		  plane->closestPoint(pos, upar, vpar, close, dist, eps, 0, seed);
+		  if (dist < tol)
+		    vec_pts[ka].push_back(remaining[kr]);
+		}
+	      
+	      std::vector<RevEngPoint*> dummy;
+	      connectedGroups(vec_pts[ka], conn_groups, false, dummy);
+
+	      int stop_break = 1;
+	    }
+    }
+  
+  // Identify largest group
+  if (conn_groups.size() == 0)
+    return false;  // No large planar component is found
+
+  int max_group = 0;
+  int ix = -1;
+  for (size_t ki=0; ki<conn_groups.size(); ++ki)
+    {
+      if ((int)conn_groups[ki].size() > max_group)
+	{
+	  max_group = (int)conn_groups[ki].size();
+	  ix = (int)ki;
+	}
+    }
+
+  if (max_group < min_size)
+    return false;   // No large planar component is found
+
+  // Extract identified points
+  if ((int)group_points_.size() > max_group)
+    {
+      for (size_t ki=0; ki<conn_groups[ix].size(); ++ki)
+	removePoint(conn_groups[ix][ki]);
+      std::swap(group_points_, conn_groups[ix]);
+    }
+  else
+    conn_groups[ix].clear();
+
+  // Update plane
+  shared_ptr<Plane> plane2 = computePlane(group_points_, vec, mainaxis);
+	  
+#ifdef DEBUG_PLANAR
+  std::ofstream of("planar_component.g2");
+  writeRegionPoints(of);
+  shared_ptr<Plane> plane3(plane2->clone());
+  double len = bbox_.low().dist(bbox_.high());
+  plane3->setParameterBounds(-len, -len, len, len);
+  plane3->write(of);
+#endif
+  
+  vector<RevEngPoint*> inpt2, outpt2;
+  vector<pair<double, double> > dist_ang2;
+  vector<double> parvals2;
+  double maxdist2, avdist2;
+  int num_inside2, num2_inside2;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  plane2, tol, maxdist2, avdist2, num_inside2, num2_inside2,
+			  inpt2, outpt2, parvals2, dist_ang2, angtol);
+  int sf_flag = defineSfFlag(0, tol, num_inside2, num2_inside2,
+			     avdist2, false);
+  if (sf_flag > ACCURACY_OK && sf_flag < NOT_SET)
+    {
+      // Check if any points should be removed
+      vector<RevEngPoint*> ang_points;
+      double dtol = std::min(0.5*tol, 1.5*avdist2);
+      identifyAngPoints(dist_ang2, 2.0*angtol, dtol, ang_points);
+#ifdef DEBUG_PLANAR
+      if (ang_points.size() > 0)
+	{
+	  std::ofstream ofa("ang_points_pc.g2");
+	  ofa << "400 1 0 4 50 50 155 255" << std::endl;
+	  ofa << ang_points.size() << std::endl;
+	  for (size_t kr=0; kr<ang_points.size(); ++kr)
+	    ofa << ang_points[kr]->getPoint() << std::endl;
+	}
+#endif
+
+      double del_frac = 0.01;
+      if ((double)ang_points.size() > del_frac*(double)group_points_.size())
+	{
+	  vector<vector<RevEngPoint*> > separate_groups;
+	  extractSpesPoints(ang_points, separate_groups, true);
+	  if (separate_groups.size() > 0)
+	    {
+	      updateInfo(tol, angtol);
+	      inpt2.clear();
+	      outpt2.clear();
+	      parvals2.clear();
+	      dist_ang2.clear();
+	      plane2 = computePlane(group_points_, avnorm_, mainaxis);
+	      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+				      plane2, tol, maxdist2, avdist2,
+				      num_inside2, num2_inside2,
+				      inpt2, outpt2, parvals2, dist_ang2, angtol);
+	    }
+	  for (size_t kr=0; kr<separate_groups.size(); ++kr)
+	    {
+	      if (separate_groups[kr].size() == 1)
+		{
+		  separate_groups[kr][0]->unsetRegion();
+		  single_pts.push_back(separate_groups[kr][0]);
+		}
+	      else
+		out_groups.push_back(separate_groups[kr]);
+	    }
+	  sf_flag = defineSfFlag(0, tol, num_inside2, num2_inside2,
+				 avdist2, false);
+	}
+    }
+
+  if (create_surface)
+    {
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  group_points_[ki]->setPar(Vector2D(parvals2[2*ki],parvals2[2*ki+1]));
+	  group_points_[ki]->setSurfaceDist(dist_ang2[ki].first, dist_ang2[ki].second);
+	}
+      setAccuracy(maxdist2, avdist2, num_inside2, num2_inside2);
+      shared_ptr<HedgeSurface> hedge(new HedgeSurface(plane2, this));
+      setHedge(hedge.get());
+      hedgesfs.push_back(hedge);
+      setSurfaceFlag(sf_flag);
+    }
+  updateInfo(tol, angtol);
+    
+   
+  // Split remaining points into connected components
+  if (conn_groups[ix].size() > 0)
+    {
+      shared_ptr<RevEngRegion> reg(new RevEngRegion(classification_type_,
+						    edge_class_type_,
+						    conn_groups[ix]));
+      vector<vector<RevEngPoint*> > connected;
+      reg->splitRegion(connected);
+      out_groups.push_back(reg->getPoints());
+      for (size_t ki=0; ki<connected.size(); ++ki)
+	{
+	  if (connected[ki].size() == 1)
+	    {
+	      connected[ki][0]->unsetRegion();
+	      single_pts.push_back(connected[ki][0]);
+	    }
+	  else
+	    out_groups.push_back(connected[ki]);
+	}
+    }
+
+
+  return true;
+}
+
+//===========================================================================
+shared_ptr<ElementarySurface>
+RevEngRegion::helicalComponent(shared_ptr<Cylinder> cyl,  double tol,
+			       double angtol, int min_point,
+			       int min_pt_reg, double avdist, int num_in2,
+			       vector<pair<double,double> >& dist_ang,
+			       vector<RevEngPoint*>& remaining,
+			       vector<vector<RevEngPoint*> >& extracted,
+			       double& maxd_out, double& avd_out,
+			       int& num_in_out, int& num2_in_out,
+			       vector<double>& parvals_out,
+			       vector<pair<double,double> >& distang_out)
+//===========================================================================
+{
+  shared_ptr<ElementarySurface> dummy_surf;
+  // Compute rotated info
+  int num_in_lin1, num_in_cub1;
+  double avdist_lin1, avdist_cub1;
+  shared_ptr<Cone> cone;
+  analyseCylRotate(cyl, tol, avdist, num_in2, avdist_lin1, num_in_lin1,
+		   avdist_cub1, num_in_cub1, cone);
+
+  // Restrict to linear cases
+  double lfac = 1.2;
+  if (avdist_lin1 > lfac*avdist_cub1)
+    return dummy_surf;
+  
+  // Identify distant points
+  double eps = 1.0e-6;
+  double dlim = 2.0*avdist;
+  vector<RevEngPoint*> dist_pts;
+  vector<RevEngPoint*> in_pts;
+  BoundingBox bb(3);
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      bool within = true;
+      Vector3D xyz = group_points_[ki]->getPoint();
+      Point pos(xyz[0], xyz[1], xyz[2]);
+      if (cone.get())
+	{
+	  double upar, vpar, dist;
+	  Point close;
+	  cone->closestPoint(pos, upar, vpar, close, dist, eps);
+	  if (dist > dlim)
+	    within = false;	  
+	}
+      else
+	{
+	  if (dist_ang[ki].first > dlim)
+	    within = false;
+	}
+
+      if (within)
+	{
+	  in_pts.push_back(group_points_[ki]);
+	  bb.addUnionWith(pos);
+	}
+      else
+	dist_pts.push_back(group_points_[ki]);
+    }
+
+  if ((int)in_pts.size() < min_point)
+    return dummy_surf;
+
+#ifdef DEBUG_CYL
+  std::ofstream of("helical_split.g2");
+  of << "400 1 0 4 0 0 255 255" << std::endl;
+  of << in_pts.size() << std::endl;
+  for (size_t ki=0; ki<in_pts.size(); ++ki)
+    of << in_pts[ki]->getPoint() << std::endl;
+  
+  of << "400 1 0 4 0 255 0 255" << std::endl;
+  of << dist_pts.size() << std::endl;
+  for (size_t ki=0; ki<dist_pts.size(); ++ki)
+    of << dist_pts[ki]->getPoint() << std::endl;
+#endif
+  
+  // Check accuracy with respect to reduced point cloud
+  // For cylinder
+  double maxdist2, avdist2;
+  int num_inside2, num2_inside2=0;
+  vector<RevEngPoint*> inpt2, outpt2;
+  vector<double> parvals2;
+  vector<pair<double, double> > dist_ang2;
+  shared_ptr<Cylinder> cyl2 = computeCylinder(in_pts, tol);
+  RevEngUtils::distToSurf(in_pts.begin(), in_pts.end(), cyl2, 
+			  tol, maxdist2, avdist2, num_inside2, num2_inside2,
+			  inpt2, outpt2, parvals2, dist_ang2, angtol);
+  int sf_flag2 = defineSfFlag((int)in_pts.size(), 0, tol, num_inside2,
+			      num2_inside2, avdist2, true);
+
+  // For cone
+  double len = bb.low().dist(bb.high());
+  Point axis = cyl2->direction();
+  Point Cx = cyl2->direction2();
+  Point loc = cyl2->location();
+  shared_ptr<Cone> cone2 =
+    RevEngUtils::coneWithAxis(in_pts, axis, Cx, loc, len);
+  
+  double maxdist3, avdist3;
+  int num_inside3, num2_inside3=0;
+  vector<RevEngPoint*> inpt3, outpt3;
+  vector<double> parvals3;
+  vector<pair<double, double> > dist_ang3;
+  RevEngUtils::distToSurf(in_pts.begin(), in_pts.end(), cone2,
+			  tol, maxdist3, avdist3, num_inside3, num2_inside3,
+			  inpt3, outpt3, parvals3, dist_ang3, angtol);
+  int sf_flag3 = defineSfFlag((int)in_pts.size(), 0, tol, num_inside3,
+			      num2_inside3, avdist3, true);
+
+  if (std::min(sf_flag2, sf_flag3) >= ACCURACY_POOR)
+    return dummy_surf;
+
+  remaining = in_pts;
+  vector<vector<RevEngPoint*> > connected;
+  vector<RevEngPoint*> dummy_inner;
+  connectedGroups(dist_pts, connected, false, dummy_inner);
+  extracted = connected;
+
+  if (sf_flag2 < sf_flag3 ||
+      (sf_flag2 == sf_flag3 &&
+       (avdist2 < avdist3 || num_inside2 > num_inside3)))
+    {
+      maxd_out = maxdist2;
+      avd_out = avdist2;
+      num_in_out = num_inside2;
+      num2_in_out = num2_inside2;
+      parvals_out = parvals2;
+      distang_out = dist_ang2;
+      return cyl2;
+    }
+  else
+    {
+      maxd_out = maxdist3;
+      avd_out = avdist3;
+      num_in_out = num_inside3;
+      num2_in_out = num2_inside3;
+      parvals_out = parvals3;
+      distang_out = dist_ang3;
+      return cone2;
+    }
+}
+
+
+//===========================================================================
+bool RevEngRegion::defineHelicalInfo(shared_ptr<Cylinder> cyl,  double tol,
+				     double angtol, int min_point,
+				     int min_pt_reg, double avdist,
+				     int num_in1, int num_in2,
+				     vector<pair<double,double> >& dist_ang,
+				     vector<shared_ptr<HedgeSurface> >& hedgesfs,
+				     vector<vector<RevEngPoint*> >& out_groups,
+				     vector<RevEngPoint*>& single_pts)
+//===========================================================================
+{
+  // Compute rotated info
+  int num_in_lin1, num_in_cub1;
+  double avdist_lin1, avdist_cub1;
+  shared_ptr<Cone> cone;
+  analyseCylRotate(cyl, tol, avdist, num_in2, avdist_lin1, num_in_lin1,
+		   avdist_cub1, num_in_cub1, cone);
+
+  // Restrict to linear cases
+  double lfac = 1.2;
+  if (avdist_lin1 > lfac*avdist_cub1)
+    return false;
+
+  // Identify distant points
+  double dlim = 2.0*avdist;
+  vector<RevEngPoint*> dist_pts;
+  vector<RevEngPoint*> remaining;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      if (dist_ang[ki].first > dlim)
+	dist_pts.push_back(group_points_[ki]);
+      else
+	remaining.push_back(group_points_[ki]);
+    }
+
+  // Check accuracy with respect to reduced point cloud
+  double maxdist2, avdist2;
+  int num_inside2, num2_inside2=0;
+  vector<RevEngPoint*> inpt2, outpt2;
+  vector<double> parvals2;
+  vector<pair<double, double> > dist_ang2;
+  shared_ptr<Cylinder> cyl2 = computeCylinder(remaining, tol);
+  RevEngUtils::distToSurf(remaining.begin(), remaining.end(),
+			  cyl2, tol, maxdist2, avdist2, num_inside2, num2_inside2,
+			  inpt2, outpt2, parvals2, dist_ang2, angtol);
+
+  // Check level of change
+  double ang = cyl->direction().angle(cyl2->direction());
+  ang = std::min(ang, M_PI-ang);
+  double distfac = 0.5;
+  double angmin = 0.1*M_PI;
+  if (ang > angmin && avdist2 > tol &&
+      (double)num2_inside2/(double)num_in2 > distfac)
+    return false;  // Not likely that the computed cylinder is a good representation
+  
+  int nfac = 2;
+  if (num2_inside2 < nfac*num_inside2 && (num_inside2 < (int)remaining.size()/2 ||
+					   avdist2 > tol))
+    return false;   // Probably not a helical surface and not sufficient accuracy for
+  // a cylinder
+
+#ifdef DEBUG_CYL
+  std::ofstream of("helical_split.g2");
+  of << "400 1 0 4 0 0 255 255" << std::endl;
+  of << remaining.size() << std::endl;
+  for (size_t ki=0; ki<remaining.size(); ++ki)
+    of << remaining[ki]->getPoint() << std::endl;
+  
+  of << "400 1 0 4 0 255 0 255" << std::endl;
+  of << dist_pts.size() << std::endl;
+  for (size_t ki=0; ki<dist_pts.size(); ++ki)
+    of << dist_pts[ki]->getPoint() << std::endl;
+#endif
+  // Set info to remaining
+  for (size_t ki=0; ki<remaining.size(); ++ki)
+    {
+      remaining[ki]->setPar(Vector2D(parvals2[2*ki],parvals2[2*ki+1]));
+      remaining[ki]->setSurfaceDist(dist_ang2[ki].first, dist_ang2[ki].second);
+    }
+  std::swap(group_points_, remaining);
+  updateInfo(tol, angtol);
+
+  // Make sure that the remaining points are connected
+  vector<vector<RevEngPoint*> > separate_groups;
+  splitRegion(separate_groups);
+  if (separate_groups.size() > 0)
+    {
+      // Recompute
+      cyl2 = computeCylinder(group_points_, tol);
+      parvals2.clear();
+      inpt2.clear();
+      outpt2.clear();
+      
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      cyl2, tol, maxdist2, avdist2,
+			      num_inside2, num2_inside2,
+			      inpt2, outpt2, parvals2, dist_ang2, angtol);
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  group_points_[ki]->setPar(Vector2D(parvals2[2*ki],parvals2[2*ki+1]));
+	  group_points_[ki]->setSurfaceDist(dist_ang2[ki].first, dist_ang2[ki].second);
+	}
+      updateInfo(tol, angtol);
+    }
+  
+  for (size_t ki=0; ki<separate_groups.size(); ++ki)
+    {
+      if (separate_groups[ki].size() == 1)
+	{
+	  separate_groups[ki][0]->unsetRegion();
+	  single_pts.push_back(separate_groups[ki][0]);
+	}
+      else
+	out_groups.push_back(separate_groups[ki]);
+    }
+
+  // Surface flag
+  int sf_flag = defineSfFlag(0, tol, num_inside2, num2_inside2,
+			     avdist2, true);
+
+  if (sf_flag < ACCURACY_POOR)
+    {
+      setAccuracy(maxdist2, avdist2, num_inside2, num2_inside2);
+      shared_ptr<HedgeSurface> hedge(new HedgeSurface(cyl2, this));
+      setHedge(hedge.get());
+      hedgesfs.push_back(hedge);
+      setSurfaceFlag(sf_flag);
+    }
+  else if (num2_inside2 >= nfac*num_inside2)
+    {
+      shared_ptr<SplineCurve> dummy;
+      sweep_ = shared_ptr<SweepData>(new SweepData(3, dummy, cyl2->location(),
+						   cyl2->direction(), maxdist2,
+						   avdist2, num_inside2,
+						   cyl2->getRadius()));
+    }
+
+  setBaseSf(cyl2, maxdist2, avdist2, num_inside2, num2_inside2);
+  
+  // Split remaining points into connected components
+  vector<vector<RevEngPoint*> > connected;
+  std::vector<RevEngPoint*> dummy;
+  connectedGroups(dist_pts, connected, false, dummy);
+  
+  for (size_t ki=0; ki<connected.size(); ++ki)
+    {
+      if (connected[ki].size() == 1)
+	{
+	  connected[ki][0]->unsetRegion();
+	  single_pts.push_back(connected[ki][0]);
+	}
+      else
+	out_groups.push_back(connected[ki]);
+    }
+
+  return true;
+}
+
+//===========================================================================
+void RevEngRegion::setAssociatedSurface(shared_ptr<ParamSurface>& surf,
+					double tol, double angtol,
+					int min_pt_reg,
+					shared_ptr<HedgeSurface>& hedge)
+//===========================================================================
+{
+  // Compute accuracy
+  double maxdist, avdist;
+  int num_in, num2_in;
+  vector<RevEngPoint*> inpt, outpt;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  surf, tol, maxdist, avdist, num_in, num2_in,
+			  inpt, outpt, parvals, dist_ang, angtol);
+
+  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		  surf->instanceType() == Class_Cone);
+  int sf_flag = defineSfFlag(0, tol, num_in, num2_in, avdist, cyllike);
+  
+  for (size_t kh=0; kh<group_points_.size(); ++kh)
+    {
+      group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+      group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+    }
+  setAccuracy(maxdist, avdist, num_in, num2_in);
+  hedge = shared_ptr<HedgeSurface>(new HedgeSurface(surf, this));
+  setHedge(hedge.get());
+  setSurfaceFlag(sf_flag);
+}
+
+//===========================================================================
+void RevEngRegion::extractOutPoints(int dir, double tol, double angtol,
+				    double angtol2,
+				    vector<vector<RevEngPoint*> >& out_groups)
+//===========================================================================
+{
+  // Compute real domain
+  double dom[4];
+  int ka;
+  int num_pt = (int)group_points_.size();
+  double dist, ang;
+  for (ka=0; ka<num_pt; ++ka)
+    {
+      Vector2D uv = group_points_[ka]->getPar();
+      group_points_[ka]->getSurfaceDist(dist, ang);
+      if (ang <= angtol2)
+	{
+	  dom[0] = dom[1] = uv[0];
+	  dom[2] = dom[3] = uv[1];
+	  break;
+	}
+    }
+  for (; ka<num_pt; ++ka)
+    {
+      Vector2D uv = group_points_[ka]->getPar();
+      group_points_[ka]->getSurfaceDist(dist, ang);
+      if (ang > angtol2)
+	continue;
+      dom[0] = std::min(dom[0], uv[0]);
+      dom[1] = std::max(dom[1], uv[0]);
+      dom[2] = std::min(dom[2], uv[1]);
+      dom[3] = std::max(dom[3], uv[1]);
+    }
+
+  vector<RevEngPoint*> out1, out2, remain;
+  for (ka=0; ka<num_pt; ++ka)
+    {
+      Vector2D uv = group_points_[ka]->getPar();
+      if (uv[dir] < dom[2*dir])
+	out1.push_back(group_points_[ka]);
+      else if (uv[dir] > dom[2*dir+1])
+	out2.push_back(group_points_[ka]);
+      else
+	remain.push_back(group_points_[ka]);
+    }
+
+  if (remain.size() < group_points_.size())
+    {
+      std::swap(remain, group_points_);
+      updateInfo(tol, angtol);
+
+      if (out1.size() > 0)
+	{
+	  vector<vector<RevEngPoint*> > con;
+	  vector<RevEngPoint*> dummy;
+	  connectedGroups(out1, con, false, dummy);
+	  for (size_t ki=0; ki<con.size(); ++ki)
+	    {
+	      for (size_t kj=0; kj<con[ki].size(); ++kj)
+		con[ki][kj]->unsetRegion();
+	    }
+	  out_groups.insert(out_groups.end(), con.begin(), con.end());
+	}
+      
+      if (out2.size() > 0)
+	{
+	  vector<vector<RevEngPoint*> > con;
+	  vector<RevEngPoint*> dummy;
+	  connectedGroups(out2, con, false, dummy);
+	  for (size_t ki=0; ki<con.size(); ++ki)
+	    {
+	      for (size_t kj=0; kj<con[ki].size(); ++kj)
+		con[ki][kj]->unsetRegion();
+	    }
+	  out_groups.insert(out_groups.end(), con.begin(), con.end());
+	}
+    }
+  }
+
+//===========================================================================
+int RevEngRegion::defineSfFlag(int min_point, double tol, int num_in, 
+			       int num_in2, double avd, bool type_cyl)
+//===========================================================================
+{
+  int sf_flag = NOT_SET;
+  double fac = 2.0;
+  bool OK = accuracyOK(0, tol, num_in, avd);
+  int nfac = 2;
+  if (OK)
+    sf_flag = ACCURACY_OK;
+  else
+    {
+      OK = accuracyOK(min_point, tol, num_in2, avd);
+      if (OK)
+	sf_flag = ANGULAR_DEVIATION;
+      else
+	{
+	  OK = accuracyOK(min_point, fac*tol, num_in2, avd);
+	  if (OK)
+	    sf_flag = ACCURACY_POOR;
+	}
+    }
+  
+  if ((sf_flag == ANGULAR_DEVIATION ||
+       (num_in2 >= nfac*num_in && sf_flag < ACCURACY_POOR)) && type_cyl)
+    sf_flag = PROBABLE_HELIX;
+
+  return sf_flag;
+}
+ 
+//===========================================================================
+int RevEngRegion::defineSfFlag(int num_points, int min_point, double tol, int num_in, 
+			       int num_in2, double avd, bool type_cyl)
+//===========================================================================
+{
+  int sf_flag = NOT_SET;
+  double fac = 2.0;
+  bool OK = accuracyOK(num_points, min_point, tol, num_in, avd);
+  int nfac = 2;
+  if (OK)
+    sf_flag = ACCURACY_OK;
+  else
+    {
+      OK = accuracyOK(num_points, min_point, tol, num_in2, avd);
+      if (OK)
+	sf_flag = ANGULAR_DEVIATION;
+      else
+	{
+	  OK = accuracyOK(num_points, min_point, fac*tol, num_in2, avd);
+	  if (OK)
+	    sf_flag = ACCURACY_POOR;
+	}
+    }
+  
+  if ((sf_flag == ANGULAR_DEVIATION ||
+       (num_in2 >= nfac*num_in && sf_flag < ACCURACY_POOR)) && type_cyl)
+    sf_flag = PROBABLE_HELIX;
+
+  return sf_flag;
+}
+ 
+//===========================================================================
+void RevEngRegion::updateInfo(double tol, double angtol)
+//===========================================================================
+{
+  maxk2_ = std::numeric_limits<double>::lowest();
+  mink2_ = std::numeric_limits<double>::max();
+  maxk1_ = std::numeric_limits<double>::lowest();
+  mink1_ = std::numeric_limits<double>::max();
+  MAH_ = MAK_ = avH_ = avK_ = 0.0;
+  bbox_ = BoundingBox(3);
+  double fac = 1.0/(double)group_points_.size();
+  for  (size_t kj=0; kj<group_points_.size(); ++kj)
+    {
+      double k1 = group_points_[kj]->minPrincipalCurvature();
+      double k2 = group_points_[kj]->maxPrincipalCurvature();
+      double H = group_points_[kj]->meanCurvature();
+      double K = group_points_[kj]->GaussCurvature();
+      mink1_ = std::min(mink1_, fabs(k1));
+      maxk1_ = std::max(maxk1_, fabs(k1));
+      mink2_ = std::min(mink2_, fabs(k2));
+      maxk2_ = std::max(maxk2_, fabs(k2));
+      avH_ += fac*H;
+      avK_ += fac*K;
+      MAH_ += fac*fabs(H);
+      MAK_ += fac*fabs(K);
+      Vector3D point = group_points_[kj]->getPoint();
+      Point point2(point[0], point[1], point[2]);
+      bbox_.addUnionWith(point2);
+    }
+
+  if (group_points_.size() > 0)
+    {
+      normalcone_ = DirectionCone(group_points_[0]->getLocFuncNormal());
+      normalcone2_ = DirectionCone(group_points_[0]->getTriangNormal());
+      avnorm_ = Point(0.0, 0.0, 0.0);
+      avnorm2_ = Point(0.0, 0.0, 0.0);
+      for  (size_t kj=0; kj<group_points_.size(); ++kj)
+	{
+	  Point norm = group_points_[kj]->getLocFuncNormal();
+	  normalcone_.addUnionWith(norm);
+	  avnorm_ += fac*norm;
+	  Point norm2 = group_points_[kj]->getTriangNormal();
+	  normalcone2_.addUnionWith(norm2);
+	  avnorm2_ += fac*norm2;
+	}
+      // (void)avnorm_.normalize_checked();
+      // (void)avnorm2_.normalize_checked();
+
+      double anglim = 0.2;
+      if (normalcone_.angle() <= anglim)
+	frac_norm_in_ = 1.0;
+      else
+	{
+	  int nmb_in = 0;
+	  for (size_t kr=0; kr<group_points_.size(); ++kr)
+	    {
+	      Point normal = group_points_[kr]->getLocFuncNormal();
+	      if (avnorm_.angle(normal) <= anglim)
+		nmb_in++;
+	    }
+	  frac_norm_in_ = (double)nmb_in/(double)group_points_.size();
+	}
+  
+      if (normalcone2_.angle() <= anglim)
+	frac_norm_in2_ = 1.0;
+      else
+	{
+	  int nmb_in = 0;
+	  for (size_t kr=0; kr<group_points_.size(); ++kr)
+	    {
+	      Point normal = group_points_[kr]->getTriangNormal();
+	      if (avnorm2_.angle(normal) <= anglim)
+		nmb_in++;
+	    }
+	  frac_norm_in2_ = (double)nmb_in/(double)group_points_.size();
+	}
+    }
+  
+  if (hasSurface() && group_points_.size() > 0)
+    {
+      if (tol > 0.0)
+	{
+	  maxdist_ = avdist_ = 0.0;
+	  num_inside_ = num_inside2_ = 0;
+	}
+      Vector2D par = group_points_[0]->getPar();
+      domain_[0] = domain_[1] = par[0];
+      domain_[2] = domain_[3] = par[1];
+      for  (size_t kj=0; kj<group_points_.size(); ++kj)
+	{
+	  Vector2D par = group_points_[kj]->getPar();
+	  domain_[0] = std::min(domain_[0], par[0]);
+	  domain_[1] = std::max(domain_[1], par[0]);
+	  domain_[2] = std::min(domain_[2], par[1]);
+	  domain_[3] = std::max(domain_[3], par[1]);
+	  if (tol > 0.0)
+	    {
+	      double dist, ang;
+	      group_points_[kj]->getSurfaceDist(dist, ang);
+	      maxdist_ = std::max(maxdist_, dist);
+	      avdist_ += fac*dist;
+	      if (dist <= tol)
+		{
+		  num_inside2_++;
+		  if (ang <= angtol)
+		    num_inside_++;
+		}
+	    }
+ 	}
+      if (tol > 0.0)
+	{
+	  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+	  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+			  surf->instanceType() == Class_Cone);
+	  surfflag_ = defineSfFlag(0, tol, num_inside_, num_inside2_, avdist_,
+				   cyllike);
+	}
+    }
+}
+
+//===========================================================================
+void RevEngRegion::addPoint(RevEngPoint* point)
+//===========================================================================
+{
+#ifdef DEBUG_CHECK
+  if (std::find(group_points_.begin(), group_points_.end(), point) != group_points_.end())
+    std::cout << "addPoint: point exists already. " << point << std::endl;
+#endif
+  int nmb = (int)group_points_.size();
+  group_points_.push_back(point);
+  point->setRegion(this);
+  Vector3D point2 = point->getPoint();
+  Point point3(point2[0], point2[1], point2[2]);
+  bbox_.addUnionWith(point3);
+  Point norm = point->getLocFuncNormal();
+  normalcone_.addUnionWith(norm);
+  Point norm2 = point->getTriangNormal();
+  normalcone2_.addUnionWith(norm2);
+  double k1 = point->minPrincipalCurvature();
+  double k2 = point->maxPrincipalCurvature();
+  mink1_ = std::min(mink1_, fabs(k1));
+  maxk1_ = std::max(maxk1_, fabs(k1));
+  mink2_ = std::min(mink2_, fabs(k2));
+  maxk2_ = std::max(maxk2_, fabs(k2));
+  avnorm_ = (nmb*avnorm_ + norm)/(double)(nmb+1);
+  double H = point->meanCurvature();
+  double K = point->GaussCurvature();
+  avH_ = (nmb*avH_ + H)/(double)(nmb+1);
+  avK_ = (nmb*avK_ + K)/(double)(nmb+1);
+  MAH_ = (nmb*MAH_ + fabs(H))/(double)(nmb+1);
+  MAK_ = (nmb*MAK_ + fabs(K))/(double)(nmb+1);
+
+  if (hasSurface())
+    {
+      Vector2D par = point->getPar();
+      domain_[0] = std::min(domain_[0], par[0]);
+      domain_[1] = std::max(domain_[1], par[0]);
+      domain_[2] = std::min(domain_[2], par[1]);
+      domain_[3] = std::max(domain_[3], par[1]);
+    }
+ 
+}
+
+//===========================================================================
+void RevEngRegion::computeDomain()
+//===========================================================================
+{
+  if (hasSurface() && group_points_.size() > 0)
+    {
+      Vector2D par = group_points_[0]->getPar();
+      domain_[0] = domain_[1] = par[0];
+      domain_[2] = domain_[3] = par[1];
+      for  (size_t kj=1; kj<group_points_.size(); ++kj)
+	{
+	  Vector2D par = group_points_[kj]->getPar();
+	  domain_[0] = std::min(domain_[0], par[0]);
+	  domain_[1] = std::max(domain_[1], par[0]);
+	  domain_[2] = std::min(domain_[2], par[1]);
+	  domain_[3] = std::max(domain_[3], par[1]);
+ 	}
+    }
+}
+
+//===========================================================================
+void RevEngRegion::removePoint(RevEngPoint* point)
+//===========================================================================
+{
+  auto found = std::find(group_points_.begin(), group_points_.end(), point);
+  if (found != group_points_.end())
+    {
+      std::swap(*found, group_points_[group_points_.size()-1]);
+      group_points_.pop_back();
+    }
+  // for (size_t ki=0; ki<group_points_.size(); ++ki)
+  //   if (group_points_[ki] == point)
+  //     {
+  // 	group_points_.erase(group_points_.begin()+ki);
+  // 	break;
+  //     }
+}
+
+//===========================================================================
+void RevEngRegion::updateWithPointsInOut(vector<RevEngPoint*>& points_out,
+					 vector<RevEngPoint*>& points_in,
+					 double tol, double angtol)
+//===========================================================================
+{
+  for (size_t ki=0; ki<points_out.size(); ++ki)
+    {
+      removePoint(points_out[ki]);
+      points_out[ki]->unsetRegion();
+    }
+
+  shared_ptr<ParamSurface> surf;
+  bool cyllike = false;
+  if (hasSurface())
+    {
+      surf = getSurface(0)->surface();
+      cyllike = (surf->instanceType() == Class_Cylinder ||
+		 surf->instanceType() == Class_Cone);
+      double maxd, avd;
+      int num_inside, num2_inside;
+      vector<RevEngPoint*> inpt, outpt;
+      vector<pair<double, double> > dist_ang;
+      vector<double> parvals;
+      RevEngUtils::distToSurf(points_in.begin(), points_in.end(),
+			      surf, tol, maxd, avd, num_inside, num2_inside,
+			      inpt, outpt, parvals, dist_ang, angtol);
+      for (size_t ki=0; ki<points_in.size(); ++ki)
+	{
+	  points_in[ki]->setPar(Vector2D(parvals[2*ki],parvals[2*ki+1]));
+	  points_in[ki]->setSurfaceDist(dist_ang[ki].first, dist_ang[ki].second);
+	}
+    }
+  for (size_t ki=0; ki<points_in.size(); ++ki)
+    addPoint(points_in[ki]);
+
+  updateInfo(tol, angtol);
+  int sf_flag = NOT_SET;
+  if (hasSurface())
+    {
+      double maxd2, avd2;
+      int num_in2, num2_in2;
+      getAccuracy(maxd2, avd2, num_in2, num2_in2);
+      sf_flag = defineSfFlag(0, tol, num_in2, num2_in2, avd2, cyllike);
+      setSurfaceFlag(sf_flag);
+    }
+}
+
+//===========================================================================
+void RevEngRegion::sortBlendPoints(vector<RevEngPoint*>& points,
+				   vector<shared_ptr<CurveOnSurface> >& cvs,
+				   double distance, bool in_blend,
+				   vector<RevEngPoint*>& blend_points)
+//===========================================================================
+{
+  int num_points = (int)points.size();
+  for (int ki=num_points-1; ki>=0; --ki)
+    {
+      double tpar, dist;
+      Point close;
+      Vector3D xyz = points[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      //int ix = -1;
+      for (size_t kj=0; kj<cvs.size(); ++kj)
+	{
+	  cvs[kj]->closestPoint(pt, cvs[kj]->startparam(), cvs[kj]->endparam(),
+				tpar, close, dist);
+	  if ((in_blend && dist < distance) ||
+	      (in_blend == false && dist >= distance))
+	    {
+	      blend_points.push_back(points[ki]);
+	      std::swap(points[ki],points[num_points-1]);
+	      --num_points;
+	    }
+	}
+    }
+
+  if (num_points < (int)points.size())
+    points.erase(points.begin()+num_points, points.end());
+}
+
+//===========================================================================
+void RevEngRegion::sortBlendPoints(vector<RevEngPoint*>& points,
+				   vector<shared_ptr<CurveOnSurface> >& cvs,
+				   double distance, RevEngRegion* other,
+				   vector<RevEngPoint*>& blend_points1,
+				   vector<RevEngPoint*>& blend_points2)
+//===========================================================================
+{
+  if ((!hasSurface()) || (!other->hasSurface()))
+    return;  // Not as expected
+
+  double eps = 1.0e-6;
+  int num_points = (int)points.size();
+  shared_ptr<ParamSurface> surf1 = getSurface(0)->surface();
+  shared_ptr<ParamSurface> surf2 = other->getSurface(0)->surface();
+  double upar1, upar2, vpar1, vpar2, dist1, dist2;
+  Point close1, close2;
+  for (int ki=num_points-1; ki>=0; --ki)
+    {
+      double tpar, dist;
+      Point close;
+      Vector3D xyz = points[ki]->getPoint();
+      Point pt(xyz[0], xyz[1], xyz[2]);
+      int ix = -1;
+      for (size_t kj=0; kj<cvs.size(); ++kj)
+	{
+	  cvs[kj]->closestPoint(pt, cvs[kj]->startparam(), cvs[kj]->endparam(),
+				tpar, close, dist);
+	  if (dist >= distance)
+	    {
+	      // Check which adjacent region the point belongs to
+	      surf1->closestPoint(pt, upar1, vpar1, close1, dist1, eps);
+	      surf2->closestPoint(pt, upar2, vpar2, close2, dist2, eps);
+	      if (dist1 <= dist2)
+		blend_points1.push_back(points[ki]);
+	      else
+		blend_points2.push_back(points[ki]);
+	      std::swap(points[ki],points[num_points-1]);
+	      --num_points;
+	    }
+	}
+    }
+
+  if (num_points < (int)points.size())
+    points.erase(points.begin()+num_points, points.end());
+}
+
+
+//===========================================================================
+void RevEngRegion::setRegionAdjacency()
+//===========================================================================
+{
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next = group_points_[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	  RevEngRegion *adj_reg = curr->region();
+	  if (adj_reg == this)
+	    continue;
+	  if (adj_reg)
+	    {
+	      adj_reg->addAdjacentRegion(this);
+	      addAdjacentRegion(adj_reg);
+	    }
+	}
+    }
+}
+
+//===========================================================================
+void RevEngRegion::updateRegionAdjacency()
+//===========================================================================
+{
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    (*it)->removeAdjacentRegion(this);
+  adjacent_regions_.clear();
+
+  setRegionAdjacency();
+}
+
+struct integrate_info
+{
+  RevEngRegion *adjacent;
+  int nmb_pt_adj;
+  double maxd, avd, maxd_adj, avd_adj;
+  int nmb_in, nmb_in_adj;
+  double min_ang, max_ang, min_dist, max_dist;
+  bool outlier;
+
+  integrate_info()
+  {
+    adjacent = 0;
+    nmb_pt_adj = nmb_in = nmb_in_adj = 0;
+    maxd = avd = maxd_adj = avd_adj = 0.0;
+    min_ang = max_ang = min_dist = max_dist = -1.0;
+    outlier = false;
+  }
+
+  void setAdjacent(RevEngRegion* adj)
+  {
+    adjacent = adj;
+  }
+
+  void setOutlier()
+  {
+    outlier = true;
+  }
+
+  void setNmbAdj(int nmb)
+  {
+    nmb_pt_adj = nmb;
+  }
+
+  void setAccuracy(double maxd1, double avd1, int nmb_in1, double maxd2,
+		   double avd2, int nmb_in2)
+  {
+    maxd = maxd1;
+    avd = avd1;
+    nmb_in = nmb_in1;
+    maxd_adj = maxd2;
+    avd_adj = avd2;
+    nmb_in_adj = nmb_in2;
+  }
+
+  void setMinMax(double min_a, double max_a, double min_d, double max_d)
+  {
+    min_ang = min_a;
+    max_ang = max_a;
+    min_dist = min_d;
+    max_dist = max_d;
+  }
+};
+  
+//===========================================================================
+bool RevEngRegion::integrateInAdjacent(double mean_edge_len, int min_next,
+				       int max_next, double tol, double angtol,
+				       int max_nmb_outlier,
+				       RevEngRegion* taboo)
+//===========================================================================
+{
+  // if (adjacent_regions_.size() != 1)
+  //   return false;   // To be removed
+
+  if ((int)group_points_.size() > max_next/2)
+    return false;
+
+#ifdef DEBUG_INTEGRATE
+  std::ofstream of("curr_integrate.g2");
+  of << "400 1 0 4 0 255 0 255" << std::endl;
+  of << group_points_.size() << std::endl;
+  for (size_t kh=0; kh<group_points_.size(); ++kh)
+    of << group_points_[kh]->getPoint() << std::endl;
+#endif 
+  size_t kj=0;
+  double local_len = group_points_[0]->getMeanEdgLen(10.0*mean_edge_len);
+  double radius = 3.0*(local_len + mean_edge_len);
+  radius = std::min(radius, 20.0*mean_edge_len);
+  size_t adjsize = adjacent_regions_.size();
+  vector<integrate_info> info(adjsize);
+  
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it, ++kj)
+    info[kj].setAdjacent(*it);
+  
+  if ((int)group_points_.size() <= max_nmb_outlier)
+    {
+      // Simplified test
+      vector<RevEngRegion*> adj_reg;
+      vector<pair<double,double> > adj_info;
+      double lentol = 2.0*mean_edge_len;
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  vector<RevEngRegion*> pt_adj_reg;
+	  vector<RevEngPoint*> pt_adj_pt;
+	  group_points_[ki]->getAdjInfo(mean_edge_len, pt_adj_reg, pt_adj_pt);
+	  Point monge1 = group_points_[ki]->getLocFuncNormal();
+	  for (size_t kj=0; kj<pt_adj_pt.size(); ++kj)
+	    {
+	      if (pt_adj_reg[kj] == this)
+		continue;
+	      double len = group_points_[ki]->pntDist(pt_adj_pt[kj]);
+	      if (len > lentol)
+		continue;
+	      Point monge2 = pt_adj_pt[kj]->getLocFuncNormal();
+	      if (monge1*monge2 < 0.0 || monge1.angle(monge2) > angtol)
+		continue;
+	      adj_reg.push_back(pt_adj_reg[kj]);
+	      adj_info.push_back(std::make_pair(len, monge1.angle(monge2)));
+	    }
+	}
+
+      for (size_t kj=0; kj<info.size(); ++kj)
+	{
+	  RevEngRegion *reg2 = info[kj].adjacent;
+	  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+	    {
+	      if (adj_reg[ki] == reg2)
+		{
+		  if (info[kj].max_dist < 0.0)
+		    {
+		      info[kj].min_dist = info[kj].max_dist = adj_info[ki].first;
+		      info[kj].min_ang = info[kj].max_ang = adj_info[ki].second;
+		    }
+		  else
+		    {
+		      info[kj].min_dist = std::min(info[kj].min_dist,adj_info[ki].first);
+		      info[kj].max_dist = std::max(info[kj].max_dist,adj_info[ki].first);
+		      info[kj].min_ang = std::min(info[kj].min_ang,adj_info[ki].second);
+		      info[kj].max_ang = std::max(info[kj].max_ang,adj_info[ki].second);
+		    }
+		}
+	    }
+	}
+    }
+
+  kj = 0;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it, ++kj)
+    {
+      double maxd1, maxd2, avd1, avd2;
+      maxd1 = maxd2 = avd1 = avd2 = std::numeric_limits<double>::max();
+      int nmb_in1 = 0, nmb_in1_2 = 0, nmb_in2 = 0;
+      bool local_approx = (info[kj].max_dist < 0.0);
+      bool outlier = false;
+      int nmb_pt_adj;
+      bool computed = computeIntegrateInfo(group_points_, *it, tol, angtol, radius,
+					   local_approx, min_next, max_next, max_nmb_outlier, 
+					   outlier, nmb_pt_adj, maxd2, avd2, nmb_in2, maxd1, 
+					   avd1, nmb_in1, nmb_in1_2);
+      info[kj].setNmbAdj(nmb_pt_adj);
+      if (outlier)
+	info[kj].setOutlier();
+      if (!computed)
+	continue;
+      // shared_ptr<ParamSurface> surf;
+      // if ((*it)->hasSurface())
+      // 	{
+      // 	  surf = (*it)->getSurface(0)->surface();
+      // 	  (*it)->getAccuracy(maxd1, avd1, nmb_in1);
+      // 	}
+      // else if ((*it)->hasBaseSf())
+      // 	(*it)->getBase(surf, maxd1, avd1, nmb_in1);
+
+      // if (surf.get())
+      // 	{
+      // 	  // Fetch accuracy of current surface
+      // 	  info[kj].setNmbAdj((*it)->numPoints());
+	  
+      // 	  // Check accuracy of new points
+      // 	  vector<RevEngPoint*> in2, out2;
+      // 	  vector<pair<double,double> > dist_ang2;
+      // 	  vector<double> parvals2;
+      // 	  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+      // 				  surf, tol, maxd2, avd2, nmb_in2, in2, out2,
+      // 				  parvals2, dist_ang2, angtol);
+      // 	}
+      // else if (info[kj].max_dist < 0.0)
+      // 	{
+      // 	  // Fetch nearby points
+      // 	  vector<RevEngPoint*> nearpts;
+      // 	  if ((*it)->numPoints() < min_next)
+      // 	    nearpts.insert(nearpts.end(), (*it)->pointsBegin(), (*it)->pointsEnd());
+      // 	  else
+      // 	    {
+      // 	      for (size_t ki=0; ki<group_points_.size(); ++ki)
+      // 		{
+      // 		  nearpts.clear();
+      // 		  group_points_[ki]->fetchClosePoints2(radius, min_next,
+      // 						       max_next-(int)group_points_.size(),
+      // 						       nearpts, *it);
+      // 		  if (nearpts.size() > max_nmb_outlier)
+      // 		    break;
+      // 		}
+      // 	    }
+      // 	  size_t nearnmb = nearpts.size();
+      // 	  info[kj].setNmbAdj((int)nearnmb);
+      // 	  if (((int)(nearnmb+group_points_.size()) <= max_nmb_outlier) ||
+      // 	      ((int)nearnmb <= max_nmb_outlier && (*it)->numPoints() > min_next))
+      // 	    {
+      // 	      if ((int)group_points_.size() <= max_nmb_outlier)
+      // 		info[kj].setOutlier();
+      // 	      continue;
+      // 	    }
+
+      // 	  nearpts.insert(nearpts.end(), group_points_.begin(), group_points_.end());
+      // 	  BoundingBox bbox = bbox_;
+      // 	  for (size_t ki=0; ki<nearnmb; ++ki)
+      // 	    {
+      // 	      Vector3D xyz = nearpts[ki]->getPoint();
+      // 	      Point xyz2(xyz[0], xyz[1], xyz[2]);
+      // 	      bbox.addUnionWith(xyz2);
+      // 	    }
+      // 	  surf = surfApprox(nearpts, bbox);
+
+      // 	  // Check accuracy
+      // 	  vector<RevEngPoint*> in1, in2, out1, out2;
+      // 	  vector<pair<double,double> > dist_ang1, dist_ang2;
+      // 	  vector<double> parvals1, parvals2;
+      // 	  RevEngUtils::distToSurf(nearpts.begin(), nearpts.begin()+nearnmb, surf,
+      // 				  tol, maxd1, avd1, nmb_in1, in1, out1,
+      // 				  parvals1, dist_ang1, angtol);
+      // 	  RevEngUtils::distToSurf(nearpts.begin()+nearnmb, nearpts.end(), surf,
+      // 				  tol, maxd2, avd2, nmb_in2, in2, out2,
+      // 				  parvals2, dist_ang2, angtol);
+      // 	}
+#ifdef DEBUG_INTEGRATE
+      int num = (*it)->numPoints();
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of << num << std::endl;
+      for (int ka=0; ka<num; ++ka)
+      	of << (*it)->getPoint(ka)->getPoint() << std::endl;
+
+      // if (surf.get())
+      // 	{
+      // 	  surf->writeStandardHeader(of);
+      // 	  surf->write(of);
+      // 	}
+#endif
+      info[kj].setAccuracy(maxd2, avd2, nmb_in2, maxd1, avd1, nmb_in1);
+    }
+
+  // Select adjacent
+  bool outlier = true;
+  int ix = -1;
+  double score = 0.0;
+  int num = (int)group_points_.size();
+  for (size_t kj=0; kj<info.size(); ++kj)
+    {
+      if (!info[kj].outlier)
+	outlier = false;
+      if (taboo && info[kj].adjacent == taboo /*&& info.size()>1*/)
+	continue;
+      if (prev_region_ && info[kj].adjacent == prev_region_ && info.size()>1)
+	continue;
+      if (info[kj].avd > tol || (info[kj].nmb_in < num/2 && info[kj].maxd > tol))
+	continue;
+      if (info[kj].adjacent->hasAssociatedBlend())
+	continue;  // Not to be extended
+      double frac1 = (double)info[kj].nmb_in/(double)num;
+      double frac2 = (double)info[kj].nmb_in_adj/(double)info[kj].nmb_pt_adj;
+      if (frac1 < 0.9*frac2 && info[kj].maxd > tol)
+	continue;
+      double avH = avH_*info[kj].adjacent->avH_;
+      double avK = avK_*info[kj].adjacent->avK_;
+      double curr_score = (tol/std::max(1.0e-6, info[kj].avd)) + /* frac2/frac1 +*/
+	(info[kj].adjacent->hasSurface()) + (avH > 0.0) + (avK > 0.0);
+      if (curr_score > score)
+	{
+	  ix = (int)kj;
+	  score = curr_score;
+	}
+    }
+
+  if (ix < 0 && (!outlier))
+    {
+      double div = std::numeric_limits<double>::max();
+      for (size_t kj=0; kj<info.size(); ++kj)
+	{
+	  if (info[kj].max_dist < 0)
+	    continue;
+	  if (taboo && info[kj].adjacent == taboo /*&& info.size()>1*/)
+	    continue;
+	  if (prev_region_ && info[kj].adjacent == prev_region_ && info.size()>1)
+	    continue;
+	  double curr_div = info[kj].min_dist + info[kj].min_ang;
+	  if (curr_div < div)
+	    {
+	      div = curr_div;
+	      ix = (int)kj;
+	    }
+	}
+    }
+  
+  if (outlier)
+    {
+     for (size_t ki=0; ki<group_points_.size(); ++ki)
+       {
+	group_points_[ki]->unsetRegion();
+	group_points_[ki]->setOutlier();
+	group_points_[ki]->addMove();
+       }
+     for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+       (*it)->removeAdjacentRegion(this);
+     //updateInfo();
+     return true;
+    }
+  else if (ix >= 0)
+    {
+      if (info[ix].adjacent->hasSurface())
+	{
+	  // Set parameter value, distance and angle difference
+	  shared_ptr<ParamSurface> surf = info[ix].adjacent->getSurface(0)->surface();
+	  double maxd2, avd2;
+	  int nmb_in2, nmb2_in2;
+	  vector<RevEngPoint*> in2, out2;
+	  vector<pair<double,double> > dist_ang2;
+	  vector<double> parvals2;
+	  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+				  surf, tol, maxd2, avd2, nmb_in2, nmb2_in2, in2, out2,
+				  parvals2, dist_ang2, angtol);
+	  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+			  surf->instanceType() == Class_Cone);
+	  int sf_flag = defineSfFlag(0, tol, nmb_in2, nmb2_in2, avd2, cyllike);
+	  for (size_t ki=0; ki<group_points_.size(); ++ki)
+	    {
+	      group_points_[ki]->setPar(Vector2D(parvals2[2*ki],parvals2[2*ki+1]));
+	      group_points_[ki]->setSurfaceDist(dist_ang2[ki].first, dist_ang2[ki].second);
+	    }
+	  setSurfaceFlag(sf_flag);
+	}
+     for (size_t ki=0; ki<group_points_.size(); ++ki)
+       group_points_[ki]->addMove();
+      vector<pair<double, double> > dummy;
+      info[ix].adjacent->addRegion(this, dummy);
+      for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+	(*it)->removeAdjacentRegion(this);
+      info[ix].adjacent->updateInfo(tol, angtol);
+      return true;
+      }
+  return false;
+}
+
+//===========================================================================
+bool RevEngRegion::computeIntegrateInfo(vector<RevEngPoint*>& points, RevEngRegion *adj_reg,
+					double tol, double angtol, double radius,
+					bool local_approx, int min_next, int max_next,
+					int max_nmb_outlier, bool& outlier, int& nmb_pt_adj,
+					double& maxdist, double& avdist, int& nmb_in,
+					double& maxdist_adj, double& avdist_adj,
+					int& nmb_in_adj, int& nmb_in_adj2)
+//===========================================================================
+{
+  outlier = false;
+  nmb_pt_adj = 0;
+  
+  shared_ptr<ParamSurface> surf;
+  if (adj_reg->hasSurface())
+    {
+      surf = adj_reg->getSurface(0)->surface();
+      adj_reg->getAccuracy(maxdist_adj, avdist_adj, nmb_in_adj, nmb_in_adj2);
+    }
+  else if (adj_reg->hasBaseSf())
+    {
+      adj_reg->getBase(surf, maxdist_adj, avdist_adj, nmb_in_adj, nmb_in_adj2);
+      nmb_in_adj2 = nmb_in_adj;
+    }
+
+  if (surf.get())
+    {
+      // Fetch accuracy of current surface
+      nmb_pt_adj = adj_reg->numPoints();
+	  
+      // Check accuracy of new points
+      vector<RevEngPoint*> in, out;
+      vector<pair<double,double> > dist_ang;
+      vector<double> parvals;
+      int nmb2_in;
+      RevEngUtils::distToSurf(points.begin(), points.end(),
+			      surf, tol, maxdist, avdist, nmb_in, nmb2_in, in, out,
+			      parvals, dist_ang, angtol);
+    }
+  else if (local_approx)
+    {
+      // Fetch nearby points
+      vector<RevEngPoint*> nearpts;
+      if (adj_reg->numPoints() < min_next)
+	nearpts.insert(nearpts.end(), adj_reg->pointsBegin(), adj_reg->pointsEnd());
+      else
+	{
+	  for (size_t ki=0; ki<points.size(); ++ki)
+	    {
+	      nearpts.clear();
+	      points[ki]->fetchClosePoints2(radius, min_next,
+					    max_next-(int)points.size(),
+					    nearpts, adj_reg);
+	      if ((int)nearpts.size() > max_nmb_outlier)
+		break;
+	    }
+	}
+      size_t nearnmb = nearpts.size();
+      nmb_pt_adj = (int)nearnmb;
+      if (((int)(nearnmb+points.size()) <= max_nmb_outlier) ||
+	  ((int)nearnmb <= max_nmb_outlier && adj_reg->numPoints() > min_next))
+	{
+	  if ((int)points.size() <= max_nmb_outlier)
+	    outlier = true;
+	  return false;
+	}
+
+      nearpts.insert(nearpts.end(), points.begin(), points.end());
+      BoundingBox bbox = bbox_;
+      for (size_t ki=0; ki<nearnmb; ++ki)
+	{
+	  Vector3D xyz = nearpts[ki]->getPoint();
+	  Point xyz2(xyz[0], xyz[1], xyz[2]);
+	  bbox.addUnionWith(xyz2);
+	}
+      surf = surfApprox(nearpts, bbox);
+
+      // Check accuracy
+      vector<RevEngPoint*> in1, in2, out1, out2;
+      vector<pair<double,double> > dist_ang1, dist_ang2;
+      vector<double> parvals1, parvals2;
+      int nmb2_in, nmb2_in_adj;
+      RevEngUtils::distToSurf(nearpts.begin(), nearpts.begin()+nearnmb, surf,
+			      tol, maxdist_adj, avdist_adj, nmb_in_adj,
+			      nmb2_in_adj, in1, out1,
+			      parvals1, dist_ang1, angtol);
+      RevEngUtils::distToSurf(nearpts.begin()+nearnmb, nearpts.end(), surf,
+			      tol, maxdist, avdist, nmb_in, nmb2_in, in2, out2,
+			      parvals2, dist_ang2, angtol);
+    }
+
+  return true;
+}
+
+//===========================================================================
+bool RevEngRegion::adjustWithCylinder(Point mainaxis[3],
+				      double tol, double angtol, int min_pt_reg,
+				      vector<vector<RevEngPoint*> >& out_groups,
+				      vector<RevEngRegion*>& grown_regions,
+				      vector<HedgeSurface*>& adj_surfs)
+//===========================================================================
+{
+  if (!hasSurface())
+    return false;
+
+  HedgeSurface *hedge = getSurface(0);
+  int code;
+  ClassType type = hedge->instanceType(code);
+  if (type != Class_Cylinder &&
+      (!(type == Class_SplineSurface && code == LINEARSWEPT_SURF)))
+    return false;
+  shared_ptr<ParamSurface> surf = hedge->surface();
+
+#ifdef DEBUG_ADJUST
+  std::ofstream of("cylinder_adjust.g2");
+  writeRegionInfo(of);
+#endif
+  
+  // Make bounding parameter domain
+  int ixd = (type == Class_Cylinder) ? 0 : 2;
+  //int ixd2 = (ixd == 0) ? 2 : 0;
+  int ixp = ixd/2;
+  double dom[4];
+  Vector2D uv = group_points_[0]->getPar();
+  dom[0] = dom[1] = uv[0];
+  dom[2] = dom[3] = uv[1];
+  double dist, ang, avang;
+  double fac = 1.0/(double)group_points_.size();
+  group_points_[0]->getSurfaceDist(dist, ang);
+  avang = fac*ang;
+  for (size_t ki=1; ki<group_points_.size(); ++ki)
+    {
+      Vector2D uv = group_points_[ki]->getPar();
+      group_points_[ki]->getSurfaceDist(dist, ang);
+      dom[0] = std::min(dom[0], uv[0]);
+      dom[1] = std::max(dom[1], uv[0]);
+      dom[2] = std::min(dom[2], uv[1]);
+      dom[3] = std::max(dom[3], uv[1]);
+      avang += fac*ang;
+    }
+
+#ifdef DEBUG_ADJUST
+  std::ofstream ofp("projected_pts.g2");
+#endif
+  
+  Point axis, pnt;
+  shared_ptr<ParamCurve> crv;
+  if (type == Class_Cylinder)
+    {
+      shared_ptr<Cylinder> cyl = dynamic_pointer_cast<Cylinder,ParamSurface>(surf);
+      axis = cyl->getAxis();
+      pnt = cyl->getLocation();
+      shared_ptr<Circle> circ(new Circle(cyl->radius(0,0), pnt, axis, cyl->direction2()));
+      crv = circ;
+    }
+  else
+    {
+      vector<Point> der(3);
+      double upar = 0.5*(dom[0]+dom[1]);
+      double vpar = 0.5*(dom[2]+dom[3]);
+      surf->point(der, upar, vpar, 1);
+      axis = der[1];
+      axis.normalize();
+      pnt = der[0];
+      vector<shared_ptr<ParamCurve> > cvs = surf->constParamCurves(vpar, true);
+      crv = cvs[0];
+    }
+  
+  vector<Point> projected;
+  double maxdp, avdp;
+  RevEngUtils::projectToPlane(group_points_, axis, pnt, projected, maxdp, avdp);
+#ifdef DEBUG_ADJUST
+  ofp << "400 1 0 4 255 0 0 255" << std::endl;
+  ofp << projected.size() << std::endl;
+  for (size_t kr=0; kr<projected.size(); ++kr)
+    ofp << projected[kr] << std::endl;
+  crv->writeStandardHeader(ofp);
+  crv->write(ofp);
+#endif
+  
+  // Reduce domain from the start
+  vector<RevEngPoint*> outer;
+  double del = 0.02;
+  double del2 = del*(dom[ixd+1] - dom[ixd]);
+  double par;
+  int knmb = 10;
+  int ka;
+  double dfac = 2.0;
+  //double afac = 2.0;
+  double pfac = 0.5;
+  int part = (int)(del*(double)group_points_.size());
+  for (ka=0, par=dom[ixd]+del2; ka<knmb; ++ka, par+=del2)
+    {
+      vector<RevEngPoint*> curr_pts;
+      double avd = 0.0, ava = 0.0;
+      int nn = 0;
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  Vector2D uv = group_points_[ki]->getPar();
+	  if (uv[ixp] >= par-del2 && uv[ixp]<par)
+	    {
+	      curr_pts.push_back(group_points_[ki]);
+	      group_points_[ki]->getSurfaceDist(dist, ang);
+	      avd += dist;
+	      ava += ang;
+	      ++nn;
+	    }
+	}
+      avd /= (double)nn;
+      ava /= (double)nn;
+
+      // Check with adjacent regions
+#ifdef DEBUG_ADJUST
+      std::ofstream ofo("outer_cand.g2");
+      ofo << "400 1 0 4 0 255 0 255" << std::endl;
+      ofo << curr_pts.size() << std::endl;
+     for (size_t ki=0; ki<curr_pts.size(); ++ki)
+       ofo << curr_pts[ki]->getPoint() << std::endl;
+#endif
+     
+      vector<RevEngRegion*> next_reg;
+      vector<int> nmb_next;
+      for (size_t ki=0; ki<curr_pts.size(); ++ki)
+	{
+	  vector<RevEngRegion*> adjr;
+	  curr_pts[ki]->adjacentRegions(adjr);
+	  for (size_t kj=0; kj<adjr.size(); ++kj)
+	    {
+	      size_t kr=0;
+	      for (kr=0; kr<next_reg.size(); ++kr)
+		if (next_reg[kr] == adjr[kj])
+		  break;
+	      if (kr == next_reg.size())
+		{
+		  next_reg.push_back(adjr[kj]);
+		  nmb_next.push_back(1);
+		}
+	      else
+		nmb_next[kr]++;
+	    }
+	}
+
+      
+      if (avd > dfac*avdist_ && (ava > dfac*avang || nn < pfac*part))
+	{
+	  outer.insert(outer.end(), curr_pts.begin(), curr_pts.end());
+	  dom[ixd] = par;
+	}
+      else
+	break;
+      int stop_break = 1;
+    }
+
+  // Reduce domain from the end
+  for (ka=0, par=dom[ixd+1]-del2; ka<knmb; ++ka, par-=del2)
+    {
+      vector<RevEngPoint*> curr_pts;
+       double avd = 0.0, ava = 0.0;
+      int nn = 0;
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  Vector2D uv = group_points_[ki]->getPar();
+	  if (uv[ixp] > par && uv[ixp]<=par+del2)
+	    {
+	      curr_pts.push_back(group_points_[ki]);
+	      group_points_[ki]->getSurfaceDist(dist, ang);
+	      avd += dist;
+	      ava += ang;
+	      ++nn;
+	    }
+	}
+      avd /= (double)nn;
+      ava /= (double)nn;
+      // Check with adjacent regions
+#ifdef DEBUG_ADJUST
+      std::ofstream ofo("outer_cand.g2");
+      ofo << "400 1 0 4 0 255 0 255" << std::endl;
+      ofo << curr_pts.size() << std::endl;
+     for (size_t ki=0; ki<curr_pts.size(); ++ki)
+       ofo << curr_pts[ki]->getPoint() << std::endl;
+#endif
+     
+      vector<RevEngRegion*> next_reg;
+      vector<int> nmb_next;
+      for (size_t ki=0; ki<curr_pts.size(); ++ki)
+	{
+	  vector<RevEngRegion*> adjr;
+	  curr_pts[ki]->adjacentRegions(adjr);
+	  for (size_t kj=0; kj<adjr.size(); ++kj)
+	    {
+	      size_t kr=0;
+	      for (kr=0; kr<next_reg.size(); ++kr)
+		if (next_reg[kr] == adjr[kj])
+		  break;
+	      if (kr == next_reg.size())
+		{
+		  next_reg.push_back(adjr[kj]);
+		  nmb_next.push_back(1);
+		}
+	      else
+		nmb_next[kr]++;
+	    }
+	}
+
+      
+      if (avd > dfac*avdist_ && (ava > dfac*avang || nn < pfac*part))
+	{
+	  outer.insert(outer.end(), curr_pts.begin(), curr_pts.end());
+	  dom[ixd+1] = par;
+	}
+      else
+	break;
+      int stop_break = 1;
+    }
+
+  // Integrate points from adjacent regions
+  vector<vector<RevEngPoint*> > adjpts;
+  vector<RevEngRegion*> adjreg;
+  vector<vector<double> > par_and_dist;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+#ifdef DEBUG_ADJUST
+      std::ofstream of2("adj_group.g2");
+      int num = (*it)->numPoints();
+      of2 << "400 1 0 4 255 0 0 255" << std::endl;
+      of2 << num << std::endl;
+      for (int ka=0; ka<num; ++ka)
+      	of2 << (*it)->getPoint(ka)->getPoint() << std::endl;
+#endif
+      
+      vector<Point> projected2;
+      double maxdp2, avdp2;
+      vector<RevEngPoint*> curr_pts = (*it)->getPoints();
+      RevEngUtils::projectToPlane(curr_pts, axis, pnt, projected2, maxdp2, avdp2);
+#ifdef DEBUG_ADJUST
+      std::ofstream ofp2("projected_pts_adj.g2");
+      ofp2 << "400 1 0 4 100 155 0 255" << std::endl;
+      ofp2 << projected2.size() << std::endl;
+      for (size_t kr=0; kr<projected2.size(); ++kr)
+	ofp2 << projected2[kr] << std::endl;
+#endif
+      
+      if ((*it)->hasSurface())
+	{
+	  shared_ptr<ParamSurface> surf2((*it)->getSurface(0)->surface()->clone());
+	  double upar2, vpar2, dist;
+	  Point close;
+	  surf2->closestPoint(pnt, upar2, vpar2, close, dist, tol);
+	  if (!surf2->isBounded())
+	    {
+	      BoundingBox bb = getBbox();
+	      double len = bb.low().dist(bb.high());
+	      shared_ptr<Cylinder> elem1 =
+		dynamic_pointer_cast<Cylinder,ParamSurface>(surf2);
+	      shared_ptr<Plane> elem2 =
+		dynamic_pointer_cast<Plane,ParamSurface>(surf2);
+	      if (elem1.get())
+		elem1->setParamBoundsV(-len, len);
+	      else if (elem2.get())
+		elem2->setParameterBounds(-len, -len, len, len);
+	    }
+
+#ifdef DEBUG_ADJUST
+	  if (surf2->isBounded())
+	    {
+	      vector<shared_ptr<ParamCurve> > cvs2_1 = surf2->constParamCurves(upar2, false);
+	      vector<shared_ptr<ParamCurve> > cvs2_2 = surf2->constParamCurves(vpar2, true);
+	      cvs2_1[0]->writeStandardHeader(ofp2);
+	      cvs2_1[0]->write(ofp2);
+	      cvs2_2[0]->writeStandardHeader(ofp2);
+	      cvs2_2[0]->write(ofp2);
+	    }
+#endif
+	}
+
+      vector<RevEngPoint*> curr_adjpts;
+       vector<double> curr_par_and_dist;
+       double avd, ava;
+       int nn;
+       getAdjInsideDist(surf, dom, tol, *it, avd, ava, nn, curr_adjpts, curr_par_and_dist);
+
+      if (avd < dfac*avdist_ /*&& (ava < dfac*avang || nn == num)*/)
+	{
+	  adjpts.push_back(curr_adjpts);
+	  adjreg.push_back(*it);
+	  par_and_dist.push_back(curr_par_and_dist);
+	}
+     }
+
+#ifdef DEBUG_ADJUST
+  std::ofstream of2("move2pts.g2");
+  for (size_t ki=0; ki<adjpts.size(); ++ki)
+    {
+      of2 << "400 1 0 4 75 75 75 255" << std::endl;
+      of2 << adjpts[ki].size() << std::endl;
+      for (size_t kh=0; kh<adjpts[ki].size(); ++kh)
+	of2 << adjpts[ki][kh]->getPoint() << std::endl;
+    }
+#endif
+  
+  // Adjust point groups
+  if (outer.size() > 0)
+    {
+      extractSpesPoints(outer, out_groups);
+      splitRegion(out_groups);
+      }
+
+  for (size_t ki=0; ki<adjpts.size(); ++ki)
+    {
+      for (size_t kh=0; kh<adjpts[ki].size(); ++kh)
+	{
+	  adjpts[ki][kh]->setMoved();
+	  adjreg[ki]->removePoint(adjpts[ki][kh]);
+	  adjpts[ki][kh]->setRegion(this);
+	  adjpts[ki][kh]->setPar(Vector2D(par_and_dist[ki][4*kh],
+					  par_and_dist[ki][4*kh+1]));
+	  adjpts[ki][kh]->setSurfaceDist(par_and_dist[ki][4*kh+2], par_and_dist[ki][4*kh+3]);
+	  addPoint(adjpts[ki][kh]);
+	}
+
+      if (adjreg[ki]->numPoints() == 0)
+	{
+	  for (auto it=adjreg[ki]->adjacent_regions_.begin();
+	       it!=adjreg[ki]->adjacent_regions_.end(); ++it)
+	    {
+	      if (*it != this)
+		{
+		  (*it)->addAdjacentRegion(this);
+		  (*it)->removeAdjacentRegion(adjreg[ki]);
+		}
+	    }
+	  grown_regions.push_back(adjreg[ki]);
+	  int num_sf = adjreg[ki]->numSurface();
+	  for (int kb=0; kb<num_sf; ++kb)
+	    adj_surfs.push_back(adjreg[ki]->getSurface(kb));
+	      
+	  removeAdjacentRegion(adjreg[ki]);
+	}
+      else
+	{
+	  if (adjreg[ki]->hasSurface())
+	    {
+	      if (adjreg[ki]->numPoints() >= min_pt_reg)
+		adjreg[ki]->checkReplaceSurf(mainaxis, min_pt_reg,
+					     tol, angtol);
+	      else
+		{
+		  int num_sf = adjreg[ki]->numSurface();
+		  for (int kb=0; kb<num_sf; ++kb)
+		    adj_surfs.push_back(adjreg[ki]->getSurface(kb));
+		  adjreg[ki]->clearSurface();
+		}
+	    }
+	  else
+	    adjreg[ki]->updateInfo(tol, angtol);
+	}
+    }
+  
+  //updateInfo();
+  checkReplaceSurf(mainaxis, min_pt_reg, tol, angtol);
+
+   
+  return (outer.size() > 0 || adjpts.size() > 0);
+}
+
+//===========================================================================
+void RevEngRegion::getAdjInsideDist(shared_ptr<ParamSurface> surf, double dom[4],
+				    double tol, RevEngRegion* reg,
+				    double& avd, double& ava, int& nn,
+				    vector<RevEngPoint*>& adjpts,
+				    vector<double>& par_and_dist)
+//===========================================================================
+{
+  double maxdd, avdd;
+  int num_inside, num2_inside;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  vector<RevEngPoint*> in, out;
+  RevEngUtils::distToSurf(reg->pointsBegin(),
+			  reg->pointsEnd(), surf, tol, maxdd, avdd, 
+			  num_inside, num2_inside, in, out, parvals, dist_ang);
+  avd = 0.0;
+  ava = 0.0;
+  nn = 0;
+  for (int kh=0; kh<(int)dist_ang.size(); ++kh)
+    {
+      if (parvals[2*kh] >= dom[0] && parvals[2*kh] <= dom[1] &&
+	  parvals[2*kh+1] >= dom[2] && parvals[2*kh+1] <= dom[3])
+	{
+	  adjpts.push_back(reg->getPoint(kh));
+	  par_and_dist.push_back(parvals[2*kh]);
+	  par_and_dist.push_back(parvals[2*kh+1]);
+	  par_and_dist.push_back(dist_ang[kh].first);
+	  par_and_dist.push_back(dist_ang[kh].second);
+	  avd += dist_ang[kh].first;
+	  ava += dist_ang[kh].second;
+	  ++nn;
+	}
+    }
+  if (nn > 0)
+    {
+      avd /= (double)nn;
+      ava /= (double)nn;
+    }
+}
+
+//===========================================================================
+void RevEngRegion::addRegion(RevEngRegion* reg,
+			     vector<pair<double, double> >& dist_ang,
+			     double maxd, double avd, int num_inside,
+			     int num_inside2)
+//===========================================================================
+{
+  int num = reg->numPoints();
+#ifdef DEBUG_CHECK
+  for (int ki=0; ki<num; ++ki)
+    {
+      auto it = std::find(group_points_.begin(), group_points_.end(), reg->getPoint(ki));
+      if (it != group_points_.end())
+	std::cout << "addRegion: point exists already. " << it-group_points_.begin() <<" " << reg << " ki= " << ki << " point: " << reg->getPoint(ki) << std::endl;
+    }
+#endif
+  
+  bbox_.addUnionWith(reg->boundingBox());
+  normalcone_.addUnionWith(reg->getNormalCone());
+  normalcone2_.addUnionWith(reg->getNormalConeTriang());
+  if (num_inside >= 0)
+    {
+      maxdist_ = std::max(maxdist_, maxd);
+      double div = (double)((int)group_points_.size() + num);
+      avdist_ = ((double)(group_points_.size())*avdist_ + num*avd)/div;
+      num_inside_ += num_inside;
+      num_inside2_ += num_inside2;
+    }
+
+  double mink1, maxk1, mink2, maxk2;
+  reg->getPrincipalCurvatureInfo(mink1, maxk1, mink2, maxk2);
+  mink1_ = std::min(mink1_, mink1);
+  maxk1_ = std::max(maxk1_, maxk1);
+  mink2_ = std::min(mink2_, mink2);
+  maxk2_ = std::max(maxk2_, maxk2);
+
+  int num_all = numPoints() + reg->numPoints();
+  double fac1 = (double)numPoints()/(double)num_all;
+  double fac2 = (double)reg->numPoints()/(double)num_all;
+  double avH, avK, MAH, MAK;
+  reg->getAvCurvatureInfo(avH, avK, MAH, MAK);
+  Point avnorm = reg->getMeanNormal();
+  Point avnorm2 = reg->getMeanNormalTriang();
+  avH_ = fac1*avH_ + fac2*avH;
+  avK_ = fac1*avK_ + fac2*avK;
+  MAH_ = fac1*MAH_ + fac2*MAH;
+  MAK_ = fac1*MAK_ + fac2*MAK;
+  avnorm_ = fac1*avnorm_ + fac2*avnorm;
+  avnorm2_ = fac1*avnorm2_ + fac2*avnorm2;
+
+  size_t kr=0;
+  for (auto it=reg->pointsBegin(); it != reg->pointsEnd(); ++it, ++kr)
+    {
+      if (dist_ang.size() > 0)
+	(*it)->setSurfaceDist(dist_ang[kr].first, dist_ang[kr].second);
+      (*it)->setRegion(this);
+    }
+  group_points_.insert(group_points_.end(), reg->pointsBegin(),
+		       reg->pointsEnd());
+  removeAdjacentRegion(reg);
+
+  if (hasSurface())
+    computeDomain();
+  
+}
+
+
+//===========================================================================
+bool RevEngRegion::includeAdjacent(RevEngRegion* adj, Point mainaxis[3], 
+				   double tol, double angtol,
+				   vector<RevEngRegion*>& grown_regions,
+				   vector<HedgeSurface*>& adj_surfs)
+//===========================================================================
+{
+  if (!hasSurface())
+    return false;
+
+  // Check accuracy
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  double maxdist, avdist;
+  int num_in, num2_in;
+  vector<RevEngPoint*> inpt, outpt;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  RevEngUtils::distToSurf(adj->pointsBegin(), adj->pointsEnd(), surf,
+			  tol, maxdist, avdist, num_in, num2_in, inpt, outpt,
+			  parvals, dist_ang, angtol);
+  
+  bool type_cyl = (surf->instanceType() == Class_Cylinder ||
+		   surf->instanceType() == Class_Cone);
+  int num_pt_adj = adj->numPoints();
+  int sf_flag = defineSfFlag(num_pt_adj, 0, tol, num_in, num2_in,
+			     avdist, type_cyl);
+  int adj_flag = adj->getSurfaceFlag();
+  if (sf_flag < NOT_SET &&
+      (sf_flag <= adj_flag ||
+       (sf_flag == PROBABLE_HELIX && adj_flag == ANGULAR_DEVIATION) ||
+       (num_in > num_pt_adj/2 && avdist <= tol)))
+    {
+      // Include
+      vector<RevEngRegion*> added_adjacent;
+      includeAdjacentRegion(adj, maxdist, avdist, num_in, num2_in,
+			    parvals, dist_ang, added_adjacent);
+      grown_regions.push_back(adj);
+      int num_sf = adj->numSurface();
+      for (int kb=0; kb<num_sf; ++kb)
+	adj_surfs.push_back(adj->getSurface(kb));
+      removeAdjacentRegion(adj);
+      setSurfaceFlag(sf_flag);
+      
+      checkReplaceSurf(mainaxis, 0, tol, angtol);
+      return true;
+    }
+  return false;
+}
+
+//===========================================================================
+void RevEngRegion::growWithSurf(Point mainaxis[3], int min_pt_reg, double tol,
+				double angtol, vector<RevEngRegion*>& grown_regions,
+				vector<HedgeSurface*>& adj_surfs,
+				vector<RevEngEdge*>& adj_edgs, bool use_base)
+//===========================================================================
+{
+  //double eps = 1.0e-6;
+  if (associated_sf_.size() == 0)
+    return;  // No surface to grow
+
+  if (hasAssociatedBlend())
+    return;
+
+  size_t num_group_points = group_points_.size();
+  
+  shared_ptr<ParamSurface> surf = associated_sf_[0]->surface();
+  int classtype = surf->instanceType();
+  bool cyllike = (classtype == Class_Cylinder || classtype == Class_Cone);
+
+  vector<RevEngRegion*> adj_reg;
+  adj_reg.insert(adj_reg.end(), adjacent_regions_.begin(), adjacent_regions_.end());
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    for (size_t kj=ki+1; kj<adj_reg.size(); ++kj)
+      if (adj_reg[kj]->numPoints() > adj_reg[ki]->numPoints())
+	std::swap(adj_reg[ki], adj_reg[kj]);
+
+  vector<grow_cand> cands;
+  bool changed = false;
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    {
+      if ((adj_reg[ki]->prev_region_ && adj_reg[ki]->prev_region_ == this) ||
+	  adj_reg[ki]->hasAssociatedBlend())
+	{
+	  continue;
+	}
+
+      int num = adj_reg[ki]->numPoints();
+      
+#ifdef DEBUG_GROW
+      int write_extend = 1;
+      std::ofstream of("curr_extend.g2");
+       if (write_extend)
+	{
+	  of << "400 1 0 4 0 255 0 255" << std::endl;
+	  of << group_points_.size() << std::endl;
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    of << group_points_[kh]->getPoint() << std::endl;
+      
+	  of << "400 1 0 4 255 0 0 255" << std::endl;
+	  of << num << std::endl;
+	  for (int ka=0; ka<num; ++ka)
+	    of << adj_reg[ki]->getPoint(ka)->getPoint() << std::endl;
+	  surf->writeStandardHeader(of);
+	  surf->write(of);
+	}
+#endif
+
+       double maxd, avd;
+       int num_in, num2_in;
+       vector<RevEngPoint*> in, out;
+       vector<pair<double, double> > dist_ang;
+       vector<double> parvals;
+       RevEngUtils::distToSurf(adj_reg[ki]->pointsBegin(),
+			       adj_reg[ki]->pointsEnd(), surf, tol,
+			       maxd, avd, num_in, num2_in,
+			       in, out, parvals, dist_ang, angtol);
+       int num_ang_in = 0;
+       double avang = 0.0;
+       double nfac = 1.0/(double)num;
+       for (size_t kh=0; kh<dist_ang.size(); ++kh)
+	 {
+	   avang += nfac*dist_ang[kh].second;
+	   if (dist_ang[kh].second <= angtol)
+	     num_ang_in++;
+	 }
+       
+       int adj_sf_flag = adj_reg[ki]->defineSfFlag(0, tol, num_in, num2_in, 
+						   avd, cyllike);
+
+       double tol_fac = 2.0;
+       double tol_fac2 = 5.0;
+       double ang_lim = 0.9;
+       double num2_lim = 0.25;
+       if (adj_sf_flag == ACCURACY_OK ||
+	   (adj_sf_flag <= surfflag_ &&
+	    avd <= tol_fac*tol && (double)num_ang_in >= ang_lim*(double)num &&
+	    (double)num2_in >= num2_lim*(double)num2_in))
+	 {
+	   // Accepted. Should possibly have a test on number of points and number
+	   // of neighbours
+	   changed = true;
+	   
+	   // Include adjacent region in present
+	   vector<RevEngRegion*> added_adjacent;
+	   includeAdjacentRegion(adj_reg[ki], maxd, avd, num_in, 
+				 num2_in, parvals, dist_ang,
+				 added_adjacent);
+	   grown_regions.push_back(adj_reg[ki]);
+
+	   int num_sf = adj_reg[ki]->numSurface();
+	   for (int kb=0; kb<num_sf; ++kb)
+	     adj_surfs.push_back(adj_reg[ki]->getSurface(kb));
+	   if (adj_reg[ki]->numRevEdges() > 0)
+	     {
+	       vector<RevEngEdge*> rev_edgs = adj_reg[ki]->getAllRevEdges();
+	       for (size_t kr=0; kr<rev_edgs.size(); ++kr)
+		 {
+		   RevEngRegion *adj1, *adj2;
+		   rev_edgs[kr]->getAdjacent(adj1, adj2);
+		   RevEngRegion *other = (adj1 == adj_reg[ki]) ? adj2 : adj1;
+		   other->removeRevEngEdge(rev_edgs[kr]);
+		 }
+	       adj_edgs.insert(adj_edgs.end(), rev_edgs.begin(), rev_edgs.end());
+	     }
+	 }
+       else if (avd <= tol_fac2*tol && (double)num_ang_in >= ang_lim*(double)num)
+	 {
+	   grow_cand curr_cand(adj_reg[ki], maxd, avd, avang, num_in, num2_in,
+			       num_ang_in);
+	   cands.push_back(curr_cand);
+	 }
+    }
+       
+  // Check if the surface should be updated
+  if (changed)
+    checkReplaceSurf(mainaxis, min_pt_reg, tol, angtol);
+	   
+  // Integrate candidate neighbours if feasible
+  if (cands.size() > 0)
+    {
+      integrateGrowCand(cands, mainaxis, tol, angtol, grown_regions,
+			adj_surfs);
+      int stop_break = 1;
+    }
+
+  double fac = 1.5;
+  if ((int)group_points_.size() > (int)(fac*(double)num_group_points) ||
+      adj_surfs.size() > 0)
+    {
+      for (size_t kj=0; kj<rev_edges_.size(); ++kj)
+	rev_edges_[kj]->increaseExtendCount();
+    }
+}
+
+
+//===========================================================================
+int RevEngRegion::getGrowAccuracy(RevEngRegion *other, double tol,
+				  double angtol, double& maxdist,
+				  double& avdist, int& num_inside, 
+				  int& num2_inside, double& maxdist2,
+				  double& avdist2, int& num_in2,
+				  int& num2_in2,vector<double>& parvals,
+				  vector<pair<double,double> >& distang)
+//===========================================================================
+{
+  if (!hasSurface())
+    return NOT_SET;
+
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  
+  vector<RevEngPoint*> in, out;
+  RevEngUtils::distToSurf(other->pointsBegin(),
+			  other->pointsEnd(), surf, tol,
+			  maxdist2, avdist2, num_in2, num2_in2,
+			  in, out, parvals, distang, angtol);
+
+  maxdist = std::max(maxdist_, maxdist2);
+  int all_pts = (int)group_points_.size() + other->numPoints();
+  double fac1 = (double)(group_points_.size())/(double)all_pts;
+  double fac2 = (double)(other->numPoints())/(double)all_pts;
+  avdist = fac1*avdist_ + fac2*avdist2;
+  num_inside = num_inside_ + num_in2;
+  num2_inside = num_inside2_ + num2_in2;
+
+  bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		  surf->instanceType() == Class_Cone);
+  int sf_flag = other->defineSfFlag(0, tol, num_in2, num2_in2, avdist2,
+				    cyllike);
+  return sf_flag;
+ }
+
+//===========================================================================
+void RevEngRegion::getDomainBoundaries(double tol, double angtol,
+				       vector<pair<int, double> >& bd_par1,
+				       vector<pair<int, double> >& bd_par2)
+//===========================================================================
+{
+  if (!hasSurface())
+    return;
+
+  // Check domain
+  double angtol2 = 1.1*angtol;
+  double dom[4];
+  int ka;
+  int num_pt = (int)group_points_.size();
+  double dist, ang;
+  for (ka=0; ka<num_pt; ++ka)
+    {
+      Vector2D uv = group_points_[ka]->getPar();
+      group_points_[ka]->getSurfaceDist(dist, ang);
+      if (ang <= angtol2)
+	{
+	  dom[0] = dom[1] = uv[0];
+	  dom[2] = dom[3] = uv[1];
+	  break;
+	}
+    }
+  for (; ka<num_pt; ++ka)
+    {
+      Vector2D uv = group_points_[ka]->getPar();
+      group_points_[ka]->getSurfaceDist(dist, ang);
+      if (ang > angtol2)
+	continue;
+      dom[0] = std::min(dom[0], uv[0]);
+      dom[1] = std::max(dom[1], uv[0]);
+      dom[2] = std::min(dom[2], uv[1]);
+      dom[3] = std::max(dom[3], uv[1]);
+    }
+
+  
+  // boundary index: 0=umin, 1=umax, 2=vmin, 3=vmax
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      shared_ptr<CurveOnSurface> sfcv =
+	dynamic_pointer_cast<CurveOnSurface,ParamCurve>(trim_edgs_[ki]->geomCurve());
+      int dir;
+      double parval;
+      if (sfcv->isConstantCurve(tol, dir, parval))
+	{
+	  bool same;
+	  int bd = sfcv->whichBoundary(tol, same);
+	  if (bd >= 0)
+	    bd_par1.push_back(std::make_pair(bd, parval));
+	}
+    }
+
+  for (size_t ki=0; ki<bd_par1.size(); ki++)
+    for (size_t kj=1; kj<bd_par1.size(); ++kj)
+      if (bd_par1[kj].first < bd_par1[ki].first)
+	std::swap(bd_par1[ki], bd_par1[kj]);
+
+  int ix=0;
+  size_t kj;
+  for (size_t ki=0; ki<bd_par1.size(); ki=kj)
+    {
+      for (kj=ki+1; kj<bd_par1.size(); ++kj)
+	if (bd_par1[ki].first != bd_par1[kj].first)
+	  break;
+      if (kj > ki+1)
+	{
+	  // Check consistence
+	  double tmin, tmax;
+	  tmin = tmax = bd_par1[ki].second;
+	  for (size_t kr=ki+1; kr<kj; ++kr)
+	    {
+	      tmin = std::min(tmin, bd_par1[kr].second);
+	      tmax = std::min(tmax, bd_par1[kr].second);
+	    }
+	  if (tmax - tmin > tol)
+	    {
+	      bd_par1.erase(bd_par1.begin()+ki, bd_par1.begin()+kj);
+	      kj = ki;
+	    }
+	  else
+	    {
+	      bd_par1[ki] = std::make_pair(bd_par1[ki].first, 0.5*(tmin+tmax));
+	      bd_par1.erase(bd_par1.begin()+ki+1, bd_par1.begin()+kj);
+	      kj = ki+1;
+	    }
+	}
+      if (bd_par1[ki].first != ix)
+	bd_par2.push_back(std::make_pair(ix, dom[ix]));
+      ++ix;
+    }
+
+  for (; ix<4; ++ix)
+    bd_par2.push_back(std::make_pair(ix, dom[ix]));
+     
+}
+
+//===========================================================================
+void RevEngRegion::growBlendSurf(vector<RevEngRegion*>& next_blend, double tol,
+				 double angtol, vector<RevEngRegion*>& grown_regions,
+				 vector<vector<RevEngPoint*> >& added_regions)
+//===========================================================================
+{
+  if (!hasSurface())
+    return;
+
+  if (!hasBlendEdge())
+    return;
+  RevEngRegion *adj1, *adj2;
+  blend_edge_->getAdjacent(adj1, adj2);
+  
+  // Set absolute and vague domain boundaries
+  vector<pair<int, double> > bd_par1;
+  vector<pair<int, double> > bd_par2;
+  getDomainBoundaries(tol, angtol, bd_par1, bd_par2);
+  if (bd_par1.size() == 0)
+    return;  // Not as expected
+
+  vector<RevEngRegion*> adj_reg;
+  adj_reg.insert(adj_reg.end(), adjacent_regions_.begin(), adjacent_regions_.end());
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    {
+      if (adj_reg[ki]->hasSurface())
+	continue;
+#ifdef DEBUG_BLEND
+      std::ofstream of("curr_blend_adj.g2");
+      adj_reg[ki]->writeRegionPoints(of);
+#endif
+      // Compute distance to current surface
+      shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+      double maxd, avd;
+      int num_in, num2_in;
+      vector<RevEngPoint*> in, out;
+      vector<pair<double, double> > dist_ang;
+      vector<double> parvals;
+      RevEngUtils::distToSurf(adj_reg[ki]->pointsBegin(),
+			      adj_reg[ki]->pointsEnd(), surf, tol,
+			      maxd, avd, num_in, num2_in,
+			      in, out, parvals, dist_ang, angtol);
+
+      vector<RevEngRegion*> other_adj;
+      adj_reg[ki]->getAdjacentRegions(other_adj);
+      if (other_adj.size() == 1 && other_adj[0] == this)
+	{
+	  // Include adjacent region in current
+	  vector<RevEngRegion*> added_adjacent;
+	  includeAdjacentRegion(adj_reg[ki], maxd, avd, num_in, 
+				num2_in, parvals, dist_ang,
+				added_adjacent);
+	  adj_reg[ki]->removeFromAdjacent();
+	  adj_reg[ki]->clearRegionAdjacency();
+	  grown_regions.push_back(adj_reg[ki]);
+
+	}
+      else
+	{
+	  // Distribute points according to identified boundaries
+	  vector<vector<int> > out1_ix(bd_par1.size());
+	  vector<int> in1_ix;
+	  double eps = 1.0e-9;
+	  int num = adj_reg[ki]->numPoints();
+	  vector<RevEngPoint*> adj_pts = adj_reg[ki]->getPoints();
+	  for (int ka=0; ka<num; ++ka)
+	    {
+	      size_t kj=0;
+	      for (kj=0; kj<bd_par1.size(); ++kj)
+		{
+		  int ix = (bd_par1[kj].first <= 1) ? 0 : 1;
+		  int sgn = (bd_par1[kj].first%2 == 0) ? -1 : 1;
+		  if ((sgn < 0 && parvals[2*ka+ix] < bd_par1[kj].second+eps) ||
+		      (sgn > 0 && parvals[2*ka+ix] > bd_par1[kj].second-eps))
+		    {
+		      out1_ix[kj].push_back(ka);
+		      break;
+		    }
+		}
+	      if (kj == bd_par1.size())
+		in1_ix.push_back(ka);
+	    }
+
+	  if (other_adj.size() == 2)
+	    {
+	      RevEngRegion* other_reg = (other_adj[0] == this) ?
+		other_adj[1] : other_adj[0];
+	      if (other_reg == adj1 || other_reg == adj2)
+		{
+		  if ((int)in1_ix.size() == num)
+		    {
+		      vector<RevEngRegion*> added_adjacent;
+		      includeAdjacentRegion(adj_reg[ki], maxd, avd, num_in, 
+					    num2_in, parvals, dist_ang,
+					    added_adjacent);
+		    }
+		  else
+		    {
+		      for (size_t kj=0; kj<in1_ix.size(); ++kj)
+			{
+			  RevEngPoint *curr = adj_pts[in1_ix[kj]];
+			  int ix = in1_ix[kj];
+			  curr->setPar(Vector2D(parvals[2*ix],
+						parvals[2*ix+1]));
+			  curr->setSurfaceDist(dist_ang[ix].first, dist_ang[ix].second);
+			  adj_reg[ki]->removePoint(curr);
+			  addPoint(curr);
+			}
+		      shared_ptr<ParamSurface> surf2 = other_reg->getSurface(0)->surface();
+		      double maxd2, avd2;
+		      int num_in2, num2_in2;
+		      vector<RevEngPoint*> in2, out2;
+		      vector<pair<double, double> > dist_ang2;
+		      vector<double> parvals2;
+		      RevEngUtils::distToSurf(adj_reg[ki]->pointsBegin(),
+					      adj_reg[ki]->pointsEnd(), surf2,
+					      tol, maxd2, avd2, num_in2,
+					      num2_in2, in2, out2, parvals2,
+					      dist_ang2, angtol);
+		      
+		      vector<RevEngRegion*> added_adjacent;
+		      other_reg->includeAdjacentRegion(adj_reg[ki], maxd2, avd2,
+						       num_in2, num2_in2,
+						       parvals2, dist_ang2,
+						       added_adjacent);
+		      
+		    }
+		  adj_reg[ki]->removeFromAdjacent();
+		  adj_reg[ki]->clearRegionAdjacency();
+		  grown_regions.push_back(adj_reg[ki]);
+		  continue;
+		}
+	    }
+	  
+	  if (in1_ix.size() == 0)
+	    continue;
+
+ 	  vector<vector<int> > out2_ix;
+	  vector<int> in2_ix;
+	  if (bd_par2.size() > 0)
+	    {
+	      out2_ix.resize(bd_par2.size());
+	      for (size_t kr=0; kr<in1_ix.size(); ++kr)
+		{
+		  int ka = in1_ix[kr];
+		  size_t kj=0;
+		  for (kj=0; kj<bd_par2.size(); ++kj)
+		    {
+		      int ix = (bd_par2[kj].first <= 1) ? 0 : 1;
+		      int sgn = (bd_par2[kj].first%2 == 0) ? -1 : 1;
+		      if ((sgn < 0 && parvals[2*ka+ix] < bd_par2[kj].second+eps) ||
+			  (sgn > 0 && parvals[2*ka+ix] > bd_par2[kj].second-eps))
+			{
+			  out2_ix[kj].push_back(ka);
+			  break;
+			}
+		    }
+		  if (kj == bd_par2.size())
+		    in2_ix.push_back(ka);
+		}
+	    }
+	  else
+	    in2_ix = in1_ix;
+
+	  // Identify grow candidates from adjacent
+	  vector<RevEngRegion*> adj_grow;
+	  vector<RevEngRegion*> remain_adj;
+	  if (in2_ix.size() < in1_ix.size())
+	    adj_grow.push_back(this);
+	  for (size_t kj=0; kj<other_adj.size(); ++kj)
+	    {
+	      if (other_adj[kj] == adj1 || other_adj[kj] == adj2)
+		adj_grow.push_back(other_adj[kj]);
+	      else
+		{
+		  size_t kh;
+		  for (kh=0; kh<next_blend.size(); ++kh)
+		    if (next_blend[kh] == other_adj[kj])
+		      break;
+		  if (kh < next_blend.size())
+		    adj_grow.push_back(other_adj[kj]);
+		  else if (other_adj[kj] != this)
+		    remain_adj.push_back(other_adj[kj]);
+		}
+	    }
+	  int glast = (int)adj_grow.size()-1;
+	  for (size_t kj=0; kj<adj_grow.size(); ++kj)
+	    if (adj_grow[kj] == adj1)
+	      {
+		if ((int)kj < glast)
+		  std::swap(adj_grow[kj], adj_grow[glast]);
+		glast--;
+	      }
+	  for (size_t kj=0; kj<adj_grow.size(); ++kj)
+	    if ((int)kj < glast && adj_grow[kj] == adj2)
+	      std::swap(adj_grow[kj], adj_grow[glast]);
+
+	  vector<vector<RevEngPoint*> > in_pts(adj_grow.size());
+	  for (size_t kj=0; kj<adj_grow.size(); ++kj)
+	    {
+	      for (size_t kh=0; kh<out2_ix.size(); ++kh)
+		if (out2_ix[kh].size() > 0)
+		  adj_grow[kj]->blendGrowFromAdjacent(adj_reg[ki], out2_ix[kh],
+						      tol, angtol, in_pts[kj]);
+	      if (adj_grow[kj] != this)
+		{
+		  for (size_t kh=0; kh<out1_ix.size(); ++kh)
+		    if (out1_ix[kh].size() > 0)
+		      adj_grow[kj]->blendGrowFromAdjacent(adj_reg[ki], out1_ix[kh],
+							  tol, angtol, in_pts[kj]);
+		}
+	    }
+
+	  for (int ka=0; ka<num; ++ka)
+	    {
+	      adj_reg[ki]->getPoint(ka)->unsetVisited();
+	    }
+
+	  for (size_t kh=0; kh<in2_ix.size(); ++kh)
+	    {
+	      int ix = in2_ix[kh];
+	      adj_pts[ix]->setPar(Vector2D(parvals[2*ix], parvals[2*ix+1]));
+	      adj_pts[ix]->setSurfaceDist(dist_ang[ix].first, dist_ang[ix].second);
+	      adj_reg[ki]->removePoint(adj_pts[ix]);
+	      addPoint(adj_pts[ix]);
+	    }
+	      
+	  for (size_t kj=0; kj<adj_grow.size(); ++kj)
+	    {
+	      if (in_pts[kj].size() > 0)
+		{
+		  shared_ptr<ParamSurface> surf2 = adj_grow[kj]->getSurface(0)->surface();
+		  double maxd2, avd2;
+		  int num_in2, num2_in2;
+		  vector<RevEngPoint*> in2, out2;
+		  vector<pair<double, double> > dist_ang2;
+		  vector<double> parvals2;
+		  RevEngUtils::distToSurf(in_pts[kj].begin(), in_pts[kj].end(),
+					  surf2, tol, maxd2, avd2, num_in2,
+					  num2_in2, in2, out2, parvals2,
+					  dist_ang2, angtol);
+		  for (size_t kh=0; kh<in_pts[kj].size(); ++kh)
+		    {
+		      in_pts[kj][kh]->setPar(Vector2D(parvals[2*kh],
+		  				  parvals[2*kh+1]));
+		      in_pts[kj][kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+		      adj_reg[ki]->removePoint(in_pts[kj][kh]);
+		      adj_grow[kj]->addPoint(in_pts[kj][kh]);
+		    }
+		  
+		}
+	      adj_grow[kj]->computeDomain();
+	    }
+
+	  if (adj_reg[ki]->numPoints() == 0)
+	    {
+	      adj_reg[ki]->removeFromAdjacent();
+	      adj_reg[ki]->clearRegionAdjacency();
+	      grown_regions.push_back(adj_reg[ki]);
+	    }
+	  else
+	    {
+	      vector<vector<RevEngPoint*> > sep_groups;
+	      adj_reg[ki]->splitRegion(sep_groups);
+	      if (sep_groups.size() > 0)
+	  	{
+	  	  added_regions.insert(added_regions.end(), sep_groups.begin(),
+	  			       sep_groups.end());
+	  	  adj_reg[ki]->updateRegionAdjacency();
+	  	}
+	    }
+
+	  if (remain_adj.size() > 0)
+	    {
+	      for (size_t kj=0; kj<remain_adj.size(); ++kj)
+		{
+		  size_t kh;
+		  for (kh=0; kh<adj_reg.size(); ++kh)
+		    if (adj_reg[kh] == remain_adj[kj])
+		      break;
+		  if (kh == adj_reg.size())
+		    adj_reg.push_back(remain_adj[kj]);
+		}
+#ifdef DEBUG_BLEND
+	      std::ofstream of2("updated_blend_grow.g2");
+	      adj_reg[ki]->writeRegionPoints(of2);
+	      for (size_t kr=0; kr<adj_grow.size(); ++kr)
+		adj_grow[kr]->writeRegionPoints(of2);
+#endif
+	    }
+	  int stop_break = 1;
+	}
+    }
+#ifdef DEBUG_BLEND
+  std::ofstream of3("updated_blend_grow2.g2");
+  writeRegionPoints(of3);
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    adj_reg[ki]->writeRegionPoints(of3);
+#endif
+  int stop_break2 = 1;
+}
+
+  
+//===========================================================================
+void RevEngRegion::blendGrowFromAdjacent(RevEngRegion* adjacent,
+					 vector<int>& pt_ix, double tol,
+					 double angtol,
+					 vector<RevEngPoint*>& grow_pt)
+//===========================================================================
+{
+  if (!hasSurface())
+    return;
+
+  vector<pair<int, double> > bd_par1;
+  vector<pair<int, double> > bd_par2;
+  if (hasBlendEdge())
+    getDomainBoundaries(tol, angtol, bd_par1, bd_par2);
+
+  double eps = 1.0e-6;
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  vector<RevEngPoint*> adj_pts = adjacent->getPoints();
+  double fac = 1.1;
+  double tol2 = std::min(fac*tol, avdist_); //fac*std::max(tol, avdist_);
+  vector<RevEngPoint*> next_pts;
+  double par[2];
+  double dist;
+  Point close;
+  Point norm, norm2, norm3;
+  vector<int> remove_ix;
+  for (int ka=(int)pt_ix.size()-1; ka>=0; --ka)
+    {
+      RevEngPoint* curr = adj_pts[pt_ix[ka]];
+      curr->setMarkIx(ka);
+      if (curr->isNeighbour(this))
+	{
+	  curr->setVisited();
+	  Vector3D xyz = curr->getPoint();
+	  Point pnt(xyz[0], xyz[1], xyz[2]);
+	  surf->closestPoint(pnt, par[0], par[1], close, dist, eps);
+	  
+	  size_t kh=0;
+	  for (kh=0; kh<bd_par1.size(); ++kh)
+	    {
+	      int ix = (bd_par1[kh].first <= 1) ? 0 : 1;
+	      int sgn = (bd_par1[kh].first%2 == 0) ? -1 : 1;
+	      if ((sgn < 0 && par[ix] < bd_par1[kh].second+eps) ||
+		  (sgn > 0 && par[ix] > bd_par1[kh].second-eps))
+		break;
+	    }
+
+	  size_t kr=0;
+	  for (kr=0; kr<bd_par2.size(); ++kr)
+	    {
+	      int ix = (bd_par2[kr].first <= 1) ? 0 : 1;
+	      int sgn = (bd_par2[kr].first%2 == 0) ? -1 : 1;
+	      if ((sgn < 0 && par[ix] < bd_par2[kr].second+eps) ||
+		  (sgn > 0 && par[ix] > bd_par2[kr].second-eps))
+		break;
+	    }
+
+	      
+	  if (kh == bd_par1.size() &&
+	      (dist <= tol2 || (bd_par2.size() > 0 && kr == bd_par2.size())))
+	    {
+	      surf->normal(norm, par[0], par[1]);
+	      norm2 = curr->getLocFuncNormal();
+	      norm3 = curr->getTriangNormal();
+	      double ang = norm.angle(norm2);
+	      double ang2 = norm.angle(norm3);
+	      ang = std::min(std::min(ang,M_PI-ang), std::min(ang2,M_PI-ang2));
+	      if (dist <= tol || ang < fac*angtol ||
+		  (bd_par2.size() > 0 && kr == bd_par2.size() && ang <= angtol))
+		{
+		  next_pts.push_back(curr);
+		  grow_pt.push_back(curr);
+		  remove_ix.push_back(ka);
+		}
+	    }
+
+	}
+    }
+
+  if (next_pts.size() == 0)
+    {
+      for (int ka=(int)pt_ix.size()-1; ka>=0; --ka)
+	{
+	  RevEngPoint* curr = adj_pts[pt_ix[ka]];
+	  curr->unsetMarkIx();
+	}
+      return;
+    }
+  
+#ifdef DEBUG_GROW
+  std::ofstream of3("seed_blendgrow.g2");
+  if (next_pts.size() > 0)
+    {
+      of3 << "400 1 0 4 255 0 0 255" << std::endl;
+      of3 << next_pts.size() << std::endl;
+      for (size_t kr=0; kr<next_pts.size(); ++kr)
+	of3 << next_pts[kr]->getPoint() << std::endl;
+    }
+  std::ofstream of4("cand_blendgrow.g2");
+  if (pt_ix.size() > 0)
+    {
+      of4 << "400 1 0 4  0 255 0 255" << std::endl;
+      of4 << pt_ix.size() << std::endl;
+      for (size_t kr=0; kr<pt_ix.size(); ++kr)
+	of4 << adj_pts[pt_ix[kr]]->getPoint() << std::endl;
+    }
+#endif
+	  
+  // Grow
+  for (size_t ki=0; ki<next_pts.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next2 = next_pts[ki]->getNeighbours();
+      for (size_t kj=0; kj<next2.size(); ++kj)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next2[kj]);
+	  if (curr->visited())
+	    continue;
+	  RevEngRegion *adj_reg = curr->region();
+	  if (adj_reg != adjacent)
+	    continue;
+	  if (curr->getMarkIx() < 0)
+	    continue;
+	  curr->setVisited();
+	  Vector3D xyz = curr->getPoint();
+	  surf->closestPoint(Point(xyz[0],xyz[1],xyz[2]), par[0], par[1], close, dist, eps);
+	  size_t kh=0;
+	  for (kh=0; kh<bd_par1.size(); ++kh)
+	    {
+	      int ix = (bd_par1[kh].first <= 1) ? 0 : 1;
+	      int sgn = (bd_par1[kh].first%2 == 0) ? -1 : 1;
+	      if ((sgn < 0 && par[ix] < bd_par1[kh].second+eps) ||
+		  (sgn > 0 && par[ix] > bd_par1[kh].second-eps))
+		break;
+	    }
+
+	  size_t kr=0;
+	  for (kr=0; kr<bd_par2.size(); ++kr)
+	    {
+	      int ix = (bd_par2[kr].first <= 1) ? 0 : 1;
+	      int sgn = (bd_par2[kr].first%2 == 0) ? -1 : 1;
+	      if ((sgn < 0 && par[ix] < bd_par2[kr].second+eps) ||
+		  (sgn > 0 && par[ix] > bd_par2[kr].second-eps))
+		break;
+	    }
+
+	  if (kh == bd_par1.size() &&
+	      (dist <= tol2 || (bd_par2.size() > 0 && kr == bd_par2.size())))
+	    {
+	      surf->normal(norm, par[0], par[1]);
+	      norm2 = curr->getLocFuncNormal();
+	      norm3 = curr->getTriangNormal();
+	      double ang = norm.angle(norm2);
+	      double ang2 = norm.angle(norm3);
+	      ang = std::min(std::min(ang,M_PI-ang), std::min(ang2,M_PI-ang2));
+	      if (dist <= tol || ang < fac*angtol ||
+		  (bd_par2.size() > 0 && kr == bd_par2.size() && ang <= angtol))
+		{
+		  grow_pt.push_back(curr);
+		  next_pts.push_back(curr);
+		  remove_ix.push_back(curr->getMarkIx());
+		}
+	    }
+	}
+    }
+
+  for (int ka=(int)pt_ix.size()-1; ka>=0; --ka)
+    {
+      RevEngPoint* curr = adj_pts[pt_ix[ka]];
+      curr->unsetMarkIx();
+    }
+
+  if (remove_ix.size() > 1)
+    std::sort(remove_ix.begin(), remove_ix.end());
+  for (int ka=(int)remove_ix.size()-1; ka>=0; --ka)
+    pt_ix.erase(pt_ix.begin()+remove_ix[ka]);
+  // for (size_t kj=0; kj<remove_ix.size(); ++kj)
+  //   {
+  //     auto it = std::find(pt_ix.begin(), pt_ix.end(), remove_ix[kj]);
+  //     if (it != pt_ix.end())
+  // 	{
+  // 	  std::swap(*it, pt_ix[pt_ix.size()-1]);
+  // 	  pt_ix.pop_back();
+  // 	}
+  //   }
+}
+
+
+//===========================================================================
+void RevEngRegion::integrateGrowCand(vector<grow_cand>& cand,
+				     Point mainaxis[3], double tol,
+				     double angtol, vector<RevEngRegion*>& grown_regions,
+				     vector<HedgeSurface*>& adj_surfs)
+//===========================================================================
+{
+  shared_ptr<ParamSurface> surf = associated_sf_[0]->surface();
+  ClassType classtype = surf->instanceType();
+  bool cyllike;
+  double tol3 = 3.0*tol;
+  double anglim = 0.75;
+
+#ifdef DEBUG_GROW
+  std::ofstream of1("source_reg.g2");
+  writeRegionPoints(of1);
+#endif
+  // Collect all points
+  // Better to keep them separate, but don't change all that code now
+  vector<RevEngPoint*> all_points;
+  all_points.insert(all_points.end(), group_points_.begin(), group_points_.end());
+  while (cand.size() > 0)
+    {
+      vector<int> all_num(cand.size()+1);
+      int num;
+      all_num[0] = num = (int)group_points_.size();
+      for (size_t ki=0; ki<cand.size(); ++ki)
+	{
+	  all_points.insert(all_points.end(), cand[ki].cand_->pointsBegin(),
+			    cand[ki].cand_->pointsEnd());
+	  all_num[ki+1] = cand[ki].cand_->numPoints();
+	  num += all_num[ki+1];
+	}
+
+      // Compute updated surface
+      shared_ptr<ParamSurface> merged1, merged2;
+      computeSurface(all_points, mainaxis, tol, angtol, classtype,
+		     merged1, merged2, cyllike);
+
+      // Check accuracy
+      vector<double> maxd(cand.size()+1), avd(cand.size()+1), avang(cand.size()+1, 0.0);
+      vector<int> num_in(cand.size()+1), num2_in(cand.size()+1), ang_in(cand.size()+1, 0);
+      vector<vector<double> > parvals(cand.size()+1);
+      vector<vector<pair<double,double> > > dist_ang(cand.size()+1);
+      vector<RevEngPoint*> inpt, outpt;
+
+      if (!merged1.get())
+	{
+	  cand.clear();
+	  break;
+	}
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(), merged1,
+			      tol, maxd[0], avd[0], num_in[0], num2_in[0], inpt,
+			      outpt, parvals[0], dist_ang[0], angtol);
+      if (merged2.get())
+	{
+	  double maxd2, avd2;
+	  int num_in2, num2_in2;
+	  vector<double> parvals2;
+	  vector<pair<double,double> > dist_ang2;
+	  vector<RevEngPoint*> inpt2, outpt2;
+	  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(), merged2,
+				  tol, maxd2, avd2, num_in2, num2_in2, inpt,
+				  outpt2, parvals2, dist_ang2, angtol);
+	  if (avd2 < avd[0] && num_in2+num2_in2 > num_in[0]+num2_in[0])
+	    {
+	      std::swap(merged1, merged2);
+	      std::swap(maxd[0], maxd2);
+	      std::swap(avd[0], avd2);
+	      std::swap(num_in[0], num_in2);
+	      std::swap(num2_in[0], num2_in2);
+	      std::swap(parvals[0], parvals2);
+	      std::swap(dist_ang[0], dist_ang2);
+	    }
+	}
+      double frac = 1.0/(double)group_points_.size();
+      for (size_t kh=0; kh<dist_ang[0].size(); ++kh)
+	  {
+	    avang[0] += frac*dist_ang[0][kh].second;
+	    if (dist_ang[0][kh].second <= angtol)
+	      ang_in[0]++;
+	  }
+
+      double maxdist = maxd[0];
+      double avdist = (double)all_num[0]*avd[0]/(double)num;
+      int num_inside = num_in[0];
+      int num2_inside = num2_in[0];
+      double meanang = (double)all_num[0]*avang[0]/(double)num;
+      int inside_ang = ang_in[0];
+      
+      for (size_t ki=0; ki<cand.size(); ++ki)
+	{
+	  inpt.clear();
+	  outpt.clear();
+	  RevEngUtils::distToSurf(cand[ki].cand_->pointsBegin(),
+				  cand[ki].cand_->pointsEnd(), merged1,
+				  tol, maxd[ki+1], avd[ki+1], num_in[ki+1], num2_in[ki+1], 
+				  inpt, outpt, parvals[ki+1], dist_ang[ki+1], angtol);
+	  frac = 1.0/(double)cand[ki].cand_->numPoints();
+	  for (size_t kh=0; kh<dist_ang[ki+1].size(); ++kh)
+	    {
+	      avang[ki+1] += frac*dist_ang[ki+1][kh].second;
+	      if (dist_ang[ki+1][kh].second <= angtol)
+		ang_in[ki+1]++;
+	    }
+
+	  maxdist = std::max(maxdist, maxd[ki+1]);
+	  avdist += (double)all_num[ki+1]*avd[ki+1]/(double)num;
+	  num_inside += num_in[ki+1];
+	  num2_inside += num2_in[ki+1];
+	  meanang += (double)all_num[ki+1]*avang[ki+1]/(double)num;
+	  inside_ang += ang_in[ki+1];
+	}
+      int sf_flag = defineSfFlag(num, 0, tol, num_inside, num2_inside, avdist,
+				 cyllike);
+#ifdef DEBUG_GROW
+      std::ofstream of2("adj_source_reg.g2");
+      for (size_t ki=0; ki<cand.size(); ++ki)
+	{
+	  cand[ki].cand_->writeRegionPoints(of2);
+	}
+#endif
+      
+      vector<size_t> out_cand;
+      for (size_t ki=0; ki<cand.size(); ++ki)
+	{
+	  if (avd[ki+1] > tol3 ||
+	      (avd[ki+1] > tol && (double)ang_in[ki+1] < anglim*(double)all_num[ki+1]))
+	    out_cand.push_back(ki);
+	}
+
+      if (sf_flag >= ACCURACY_POOR && out_cand.size() == 0)
+	{
+	  double max_av = avd[1];
+	  size_t ix = 0;
+	  for (size_t ki=1; ki<cand.size(); ++ki)
+	    if (avd[ki+1] > max_av)
+	      {
+		max_av = avd[ki+1];
+		ix = ki;
+	      }
+
+	  out_cand.push_back(ix);
+	}
+
+      if (out_cand.size() == 0)
+	{
+	  // Integrate
+	  for (size_t kh=0; kh<group_points_.size(); ++kh)
+	    {
+	      group_points_[kh]->setPar(Vector2D(parvals[0][2*kh],parvals[0][2*kh+1]));
+	      group_points_[kh]->setSurfaceDist(dist_ang[0][kh].first,
+						dist_ang[0][kh].second);
+	    }
+	  for (size_t ki=0; ki<cand.size(); ++ki)
+	    {
+	      vector<RevEngRegion*> added_adjacent;
+	      includeAdjacentRegion(cand[ki].cand_, maxd[ki+1], avd[ki+1],
+				    num_in[ki+1], num2_in[ki+1],
+				    parvals[ki+1], dist_ang[ki+1], added_adjacent);
+	      grown_regions.push_back(cand[ki].cand_);
+	      int num_sf = cand[ki].cand_->numSurface();
+	      for (int kb=0; kb<num_sf; ++kb)
+		adj_surfs.push_back(cand[ki].cand_->getSurface(kb));
+	    }
+	  associated_sf_[0]->replaceSurf(merged1);
+	  if (!merged1->isBounded())
+	    {
+	      double diag = bbox_.low().dist(bbox_.high());
+	      associated_sf_[0]->limitSurf(2*diag);
+	    }
+	  updateInfo(tol, angtol);
+	  setSurfaceFlag(sf_flag);
+	  for (size_t kj=0; kj<rev_edges_.size(); ++kj)
+	    rev_edges_[kj]->replaceSurf(this, merged1, tol);
+
+	  break;
+	}
+      else
+	{
+	  for (int ka=(int)out_cand.size()-1; ka>=0; --ka)
+	    cand.erase(cand.begin()+ka);
+	}
+      int stop_break0 = 1;
+    }
+  
+  int stop_break = 1;
+}
+
+
+//===========================================================================
+bool RevEngRegion::mergePlanarReg(double zero_H, double zero_K, double tol,
+				  Point mainaxis[3],
+				  vector<RevEngRegion*>& grown_regions)
+//===========================================================================
+{
+  if (!feasiblePlane(zero_H, zero_K))
+    return false;
+#ifdef DEBUG_MERGE
+  std::ofstream of1("source_planar_reg.g2");
+  writeRegionPoints(of1);
+#endif
+  
+  double anglim = 0.1*M_PI; //0.05;
+  vector<RevEngRegion*> merge_cand;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      if (!(*it)->feasiblePlane(zero_H, zero_K))
+	continue;
+      if ((*it)->hasSurface())
+	continue;
+      if ((*it)->hasAssociatedBlend())
+	continue;
+
+      Point norm = (*it)->getMeanNormal();
+      double ang = avnorm_.angle(norm);
+      if (ang <= anglim)
+	merge_cand.push_back(*it);
+    }
+
+  if (merge_cand.size() == 0)
+    return false;
+  
+#ifdef DEBUG_MERGE
+  std::ofstream of2("adj_planar_reg.g2");
+  for (size_t ki=0; ki<merge_cand.size(); ++ki)
+    merge_cand[ki]->writeRegionPoints(of2);
+#endif
+
+  // Check accuracy
+  vector<RevEngPoint*> all_pts;
+  all_pts.insert(all_pts.end(), group_points_.begin(), group_points_.end());
+  for (size_t ki=0; ki<merge_cand.size(); ++ki)
+    all_pts.insert(all_pts.end(), merge_cand[ki]->pointsBegin(),
+		   merge_cand[ki]->pointsEnd());
+  shared_ptr<Plane> plane = computePlane(all_pts, avnorm_, mainaxis);
+
+  double angtol = -1.0;
+  int min_pt = 2;  // Not crucial here
+  double maxdist, avdist;
+  int num_in, num2_in;
+  vector<pair<double, double> > dist_ang;
+  vector<double> parvals;
+  vector<RevEngPoint*> inpt, outpt;
+  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			  plane, tol, maxdist, avdist, num_in, num2_in, inpt,
+			  outpt, parvals, dist_ang, angtol);
+  if (!accuracyOK(min_pt, tol, num_in, avdist))
+    return false;
+
+  for (int kj=(int)merge_cand.size()-1; kj>=0; --kj)
+    {
+      double maxdist2, avdist2;
+      int num_in2, num2_in2;
+      vector<pair<double, double> > dist_ang2;
+      vector<double> parvals2;
+      vector<RevEngPoint*> inpt2, outpt2;
+      RevEngUtils::distToSurf(merge_cand[kj]->pointsBegin(),
+			      merge_cand[kj]->pointsEnd(),
+			      plane, tol, maxdist2, avdist2, num_in2, num2_in2, inpt2,
+			      outpt2, parvals2, dist_ang2, angtol);
+      if (!merge_cand[kj]->accuracyOK(0, tol, num_in2, avdist2))
+	merge_cand.erase(merge_cand.begin()+kj);	  
+    }
+  if (merge_cand.size() == 0)
+    return false;
+
+  // Include next layer of adjacent regions
+  vector<RevEngRegion*> adj_reg;
+  size_t cand_size = merge_cand.size();
+  Point dir = plane->direction();
+  for (size_t ki=0; ki<cand_size; ++ki)
+    {
+      for (auto it=merge_cand[ki]->adjacent_regions_.begin();
+  	     it!=merge_cand[ki]->adjacent_regions_.end(); ++it)
+  	{
+  	  if ((*it) == this)
+  	    continue;
+	  if (!(*it)->feasiblePlane(zero_H, zero_K))
+	    continue;
+	  if ((*it)->hasSurface())
+	    continue;
+	  if ((*it)->hasAssociatedBlend())
+	    continue;
+      
+  	  size_t kr;
+  	  for (kr=0; kr<merge_cand.size(); ++kr)
+  	    if ((*it) == merge_cand[kr])
+  	      break;
+	  if (kr < merge_cand.size())
+	    continue;
+
+  	  Point norm = (*it)->getMeanNormal();
+  	  double ang = dir.angle(norm);
+  	  if (ang > anglim)
+  	    continue;
+  	  double maxdist3, avdist3;
+  	  int num_in3, num2_in3;
+  	  vector<pair<double, double> > dist_ang3;
+  	  vector<double> parvals3;
+  	  vector<RevEngPoint*> inpt3, outpt3;
+  	  RevEngUtils::distToSurf((*it)->pointsBegin(),
+  				  (*it)->pointsEnd(),
+  				  plane, tol, maxdist3, avdist3, num_in3, num2_in3, inpt3,
+  				  outpt3, parvals3, dist_ang3, angtol);
+  	  if ((*it)->accuracyOK(0, tol, num_in3, avdist3))
+  	    merge_cand.push_back(*it);
+  	}
+    }
+
+#ifdef DEBUG_MERGE
+  std::ofstream of3("adj_planar2_reg.g2");
+  for (size_t ki=0; ki<merge_cand.size(); ++ki)
+    merge_cand[ki]->writeRegionPoints(of3);
+#endif
+
+  // Integrate identified regions
+  for (size_t ki=0; ki<merge_cand.size(); ++ki)
+    {
+      grown_regions.push_back(merge_cand[ki]);
+      for (auto it=merge_cand[ki]->adjacent_regions_.begin();
+	       it!=merge_cand[ki]->adjacent_regions_.end(); ++it)
+	{
+	  if (*it != this)
+	    {
+	      addAdjacentRegion(*it);
+	      (*it)->addAdjacentRegion(this);
+	      (*it)->removeAdjacentRegion(merge_cand[ki]);
+	    }
+	}
+      // for (size_t kj=ki+1; kj<merge_cand.size(); ++kj)
+      // 	if (merge_cand[kj]->isAdjacent(merge_cand[ki]))
+      // 	  merge_cand[kj]->removeAdjacentRegion(merge_cand[ki]);
+      std::vector<std::pair<double, double> > dummy;
+      addRegion(merge_cand[ki], dummy);
+      removeAdjacentRegion(merge_cand[ki]);
+    }
+
+  return true;
+}
+
+//===========================================================================
+void RevEngRegion::mergeAdjacentSimilar(double tol, double angtol,
+					vector<RevEngRegion*>& grown_regions,
+					vector<HedgeSurface*>& adj_surfs,
+					vector<RevEngEdge*>& adj_edgs)
+//===========================================================================
+{
+  if (associated_sf_.size() == 0)
+    return;  // No surface with which to check growt
+
+  if (hasAssociatedBlend())
+    return;
+
+#ifdef DEBUG_MERGE
+  std::ofstream of1("source_group.g2");
+  writeRegionPoints(of1);
+#endif
+  int sfcode;
+  ClassType classtype = associated_sf_[0]->instanceType(sfcode);
+  bool cyllike = (classtype == Class_Cylinder || classtype == Class_Cone);
+  //HedgeSurface *hedge = associated_sf_[0];
+  vector<RevEngRegion*> adj_reg;
+  vector<double> score;
+  double frac2 = 0.75;
+  double frac3 = 2.0;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      if ((*it)->hasSurface() && (!(*it)->hasAssociatedBlend()))
+	{
+	  double curr_score;
+	  bool compatible = associated_sf_[0]->isCompatible((*it)->getSurface(0),
+							    angtol, tol,
+							    classtype, curr_score);
+	  if (compatible)
+	    {
+	      adj_reg.push_back(*it);
+	      score.push_back(curr_score);
+	    }
+	}
+    }
+
+  if (adj_reg.size() == 0)
+    return; // Nothing with which to merge
+
+#ifdef DEBUG_MERGE
+  std::ofstream of2("adj_group.g2");
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    adj_reg[ki]->writeRegionPoints(of2);
+#endif
+  for (size_t ki=0; ki<adj_reg.size(); ++ki)
+    for (size_t kj=ki+1; kj<adj_reg.size(); ++kj)
+      if (score[kj] > score[ki])
+	{
+	  std::swap(score[ki], score[kj]);
+	  std::swap(adj_reg[ki], adj_reg[kj]);
+	}
+
+  shared_ptr<ParamSurface> surf;
+  int ka;
+  double maxdist=0.0, avdist=0.0;
+  int num_in = 0;
+  vector<double> maxd(adj_reg.size()+1, 0.0);
+  vector<double> avd(adj_reg.size()+1, 0.0);
+  vector<int> ninside(adj_reg.size()+1, 0);
+  vector<int> ninside2(adj_reg.size()+1, 0);
+  vector<vector<double> > parvals(adj_reg.size()+1);
+  vector<vector<pair<double,double> > > dist_ang(adj_reg.size()+1);
+  for (ka=(int)adj_reg.size(); ka>=1; --ka)
+    {
+      // Create surface from combined point set
+      vector<pair<vector<RevEngPoint*>::iterator,
+		  vector<RevEngPoint*>::iterator> > points;
+      BoundingBox bbox(3);
+      vector<int> nmbpts;
+
+      nmbpts.push_back(numPoints());
+      points.push_back(std::make_pair(pointsBegin(), pointsEnd()));
+      bbox = boundingBox();
+      
+      for (int kb=0; kb<ka; ++kb)
+	{
+	  nmbpts.push_back(adj_reg[kb]->numPoints());
+	  points.push_back(std::make_pair(adj_reg[kb]->pointsBegin(),
+					  adj_reg[kb]->pointsEnd()));
+	  bbox.addUnionWith(adj_reg[kb]->boundingBox());
+	}
+      
+      int num_all = 0;
+      for (int kb=0; kb<=ka; ++kb)
+	num_all += nmbpts[kb];
+      double frac = 1.0/(double)num_all;
+      if (classtype == Class_Plane)
+	{
+	  surf = RevEngUtils::doMergePlanes(points, bbox, nmbpts);
+	}
+      else if (classtype == Class_Cylinder)
+	{
+	  surf = RevEngUtils::doMergeCylinders(points, bbox, nmbpts);
+	}
+      else if (classtype == Class_Sphere)
+	{
+	  Point normal = frac*numPoints()*avnorm_;
+	  
+	  for (int kb=1; kb<=ka; ++kb)
+	    normal += frac*adj_reg[kb-1]->numPoints()*adj_reg[kb-1]->getMeanNormal();
+	  normal.normalize_checked();
+	  surf = RevEngUtils::doMergeSpheres(points, bbox, nmbpts, normal);
+	}
+      else if (classtype == Class_Torus)
+	{
+	  surf = RevEngUtils::doMergeTorus(points, bbox, nmbpts);
+	}
+      if (!surf.get())
+	continue;
+
+      // Check accuracy
+#ifdef DEBUG_GROW
+       std::ofstream of("in_out_adj.g2");
+#endif
+       maxdist = avdist = 0.0;
+       num_in = 0;
+       int num2_in = 0;
+       vector<int> sfflag(ka+1, NOT_SET);
+       bool flagOK = true;
+      for (int kb=0; kb<=ka; ++kb)
+	{
+	  maxd[kb] = avd[kb] = 0.0;
+	  ninside[kb] = ninside2[kb] = 0;
+	  parvals[kb].clear();
+	  dist_ang[kb].clear();
+	  vector<RevEngPoint*> in, out;
+	  RevEngUtils::distToSurf(points[kb].first, points[kb].second, surf, tol,
+				  maxd[kb], avd[kb], ninside[kb], ninside2[kb],
+				  in, out, parvals[kb],
+				  dist_ang[kb], angtol);
+	  maxdist = std::max(maxdist, maxd[kb]);
+	  avdist += frac*nmbpts[kb]*avd[kb];
+	  num_in += ninside[kb];
+	  num2_in += ninside2[kb];
+	  RevEngRegion *curr = (kb==0) ? this : adj_reg[kb-1];
+	  sfflag[kb] = curr->defineSfFlag(0, tol, ninside[kb], ninside2[kb],
+					  avd[kb], cyllike);
+	  if (sfflag[kb] == NOT_SET)
+	    flagOK = false;
+
+#ifdef DEBUG_GROW
+	  of << "400 1 0 4 255 0 0 255" << std::endl;
+	  of << in.size() << std::endl;
+	  for (size_t kj=0; kj<in.size(); ++kj)
+	    of << in[kj]->getPoint() << std::endl;
+	  of << "400 1 0 4 0 255 0 255" << std::endl;
+	  of << out.size() << std::endl;
+	  for (size_t kj=0; kj<out.size(); ++kj)
+	    of << out[kj]->getPoint() << std::endl;
+#endif
+	}
+
+      int num = numPoints();
+      double init_max = frac*num*maxdist_;
+      double init_av = frac*num*avdist_;
+      int init_in = num_inside_;
+      int init_in2 = num_inside2_;
+      for (int kb=0; kb<ka; ++kb)
+	{
+	  int num2 = adj_reg[kb]->numPoints();
+	  double max2, av2;
+	  int num_in2, num2_in2;
+	  adj_reg[kb]->getAccuracy(max2, av2, num_in2, num2_in2);
+	  init_max += frac*num2*max2;
+	  init_av += frac*num2*av2;
+	  init_in += num_in2;
+	  init_in2 += num2_in2;
+	}
+      if (flagOK && num2_in > num_all/2 && avdist < tol &&
+	  (double)num2_in > frac2*(double)init_in2 /*&&
+						     (avdist < frac3*init_av || avdist < frac2*tol)*/)
+	break;
+
+      // Swap adjacent regions to skip the least accurate region
+      if (ka > 1 && avd[1] > avd[ka])  // The test should be made more accurate
+	{
+	  std::swap(adj_reg[0], adj_reg[ka-1]);
+	  std::swap(score[0], score[ka-1]);
+	}
+    }
+
+  if (!surf.get())
+    return;
+  if (ka >= 1)
+    {
+      setAccuracy(maxd[0], avd[0], ninside[0], ninside2[0]);
+      for (size_t ki=0; ki<group_points_.size(); ++ki)
+	{
+	  group_points_[ki]->setPar(Vector2D(parvals[0][2*ki],parvals[0][2*ki+1]));
+	  group_points_[ki]->setSurfaceDist(dist_ang[0][ki].first, dist_ang[0][ki].second);
+	}
+      
+      for (int kb=0; kb<ka; ++kb)
+	{
+	  vector<RevEngRegion*> added_adjacent;
+	  includeAdjacentRegion(adj_reg[kb], maxd[kb+1], avd[kb+1], ninside[kb+1],
+				ninside2[kb+1], parvals[kb+1],
+			dist_ang[kb+1], added_adjacent);
+	  grown_regions.push_back(adj_reg[kb]);
+	  int num_sf = adj_reg[kb]->numSurface();
+	  for (int kc=0; kc<num_sf; ++kc)
+	    adj_surfs.push_back(adj_reg[kb]->getSurface(kc));
+	  vector<RevEngEdge*> rev_edgs = adj_reg[kb]->getAllRevEdges();
+	  for (size_t kr=0; kr<rev_edgs.size(); ++kr)
+	    {
+	      RevEngRegion *adj1, *adj2;
+	      rev_edgs[kr]->getAdjacent(adj1, adj2);
+	      RevEngRegion *other = (adj1 == adj_reg[kb]) ? adj2 : adj1;
+	      other->removeRevEngEdge(rev_edgs[kr]);
+	    }
+	  adj_edgs.insert(adj_edgs.end(), rev_edgs.begin(), rev_edgs.end());
+	  removeAdjacentRegion(adj_reg[kb]);
+	}
+      updateInfo(tol, angtol);
+      double maxdist2, avdist2;
+      int num_in2, num2_in2;
+      getAccuracy(maxdist2, avdist2, num_in2, num2_in2);
+      int surfflag = defineSfFlag(0, tol, num_in2, num2_in2, avdist2, cyllike);
+      setSurfaceFlag(surfflag);
+      associated_sf_[0]->replaceSurf(surf);
+      computeDomain();
+    }
+  
+}
+
+//===========================================================================
+void
+RevEngRegion::includeAdjacentRegion(RevEngRegion* reg, double maxd, double avd,
+				    int num_inside, int num_inside2,
+				    vector<double>& parvals,
+				    vector<pair<double, double> >& dist_ang,
+				    vector<RevEngRegion*>& added_adjacent)
+//===========================================================================
+{
+  // First update parameter values
+  size_t kr=0;
+  for (auto it1=reg->pointsBegin(); it1!=reg->pointsEnd(); ++it1, kr+=2)
+    {
+      (*it1)->addMove();
+      (*it1)->setPar(Vector2D(parvals[kr],parvals[kr+1]));
+    }
+  addRegion(reg, dist_ang, maxd, avd, num_inside, num_inside2);
+
+
+  // Update adjacent regions
+  for (auto it2=reg->adjacent_regions_.begin(); it2!=reg->adjacent_regions_.end(); ++it2)
+    {
+      if (*it2 != this)
+	{
+	  size_t nmb_adj = adjacent_regions_.size();
+	  addAdjacentRegion(*it2);
+	  if (adjacent_regions_.size() > nmb_adj)
+	    {
+	      added_adjacent.push_back(*it2);
+	      (*it2)->addAdjacentRegion(this);
+	    }
+	  (*it2)->removeAdjacentRegion(reg);
+	}
+    }
+}
+
+//===========================================================================
+void RevEngRegion::checkEdgeAssociation(double tol, int min_point_reg,
+					vector<HedgeSurface*>& removed_sfs)
+//===========================================================================
+{
+  vector<RevEngRegion*> adj_regs(adjacent_regions_.begin(), adjacent_regions_.end());
+  std::set<RevEngEdge*> rev_edgs;
+  for (size_t ki=0; ki<adj_regs.size(); ++ki)
+    if (adj_regs[ki]->hasRevEdges())
+      {
+	vector<RevEngEdge*> curr_edgs = adj_regs[ki]->getAllRevEdges();
+	rev_edgs.insert(curr_edgs.begin(), curr_edgs.end());
+      }
+  vector<RevEngEdge*> edges(rev_edgs.begin(), rev_edgs.end());
+  for (size_t ki=0; ki<edges.size(); ++ki)
+    {
+      vector<RevEngRegion*> blend_regs;
+      edges[ki]->getAllBlendRegs(blend_regs);
+      size_t kj = 0;
+      for (kj=0; kj<blend_regs.size(); ++kj)
+	if (blend_regs[kj]->isAdjacent(this))
+	  {
+	    //std::cout << "Potential edge" << std::endl;
+	    vector<shared_ptr<CurveOnSurface> > cvs;
+	    edges[ki]->getCurve(cvs);
+	    double width = edges[ki]->getDistance();
+	    int num_in_blend = 0;
+	    if (isInBlend(cvs, 2.0*width, tol, num_in_blend))
+	      {
+		edges[ki]->addBlendRegion(this);
+		setAssociatedBlend(edges[ki]);
+	      }
+	    else if (num_in_blend > 0 && (int)group_points_.size() < min_point_reg)
+	      {
+		// Dubious surface
+		if (associated_sf_.size() > 0)
+		  removed_sfs.insert(removed_sfs.end(), associated_sf_.begin(),
+				     associated_sf_.end());
+		clearSurface();
+	      }
+	    break;
+	  }
+      if (kj < blend_regs.size())
+	break;
+    }
+  int stop_break = 1;
+}
+
+//===========================================================================
+
+void endPoints(vector<RevEngPoint*>& seq_pts, RevEngPoint*& end1,
+		    RevEngPoint*& end2)
+{
+  size_t ix1 = 0, ix2 = 1;
+  ix2 = std::min(ix2, seq_pts.size()-1);
+  double maxlen = seq_pts[ix1]->pntDist(seq_pts[ix2]);
+  for (size_t ki=0; ki<seq_pts.size(); ++ki)
+    for (size_t kj=ki+1; kj<seq_pts.size(); ++kj)
+      {
+	double len = seq_pts[ki]->pntDist(seq_pts[kj]);
+	if (len > maxlen)
+	  {
+	    maxlen = len;
+	    ix1 = ki;
+	    ix2 = kj;
+	  }
+      }
+  end1 = seq_pts[ix1];
+  end2 = seq_pts[ix2];
+}
+
+void getBranches(vector<vector<RevEngPoint*> >& bd_pts,
+		 vector<vector<pair<RevEngPoint*,int> > >& branches)
+{
+  RevEngPoint *dummy = 0;
+  branches.resize(bd_pts.size());
+  for (size_t ki=0; ki<bd_pts.size(); ++ki)
+    {
+      for (size_t kr=ki+1; kr<bd_pts.size(); ++kr)
+	{
+	  for (size_t kj=0; kj<bd_pts[ki].size(); ++kj)
+	    {
+	      for (size_t kh=0; kh<bd_pts[kr].size(); ++kh)
+		{
+		  if (bd_pts[ki][kj]->isNeighbour(bd_pts[kr][kh]))
+		    {
+		      branches[ki].push_back(std::make_pair(bd_pts[ki][kj],(int)kr));
+		      branches[kr].push_back(std::make_pair(bd_pts[kr][kh],(int)ki));
+		    }
+		}
+	    }
+	}
+      if (branches[ki].size() == 0)
+	branches[ki].push_back(std::make_pair(dummy,-1));
+    }
+}
+
+int getNextSeq(vector<int>& prev, int curr,
+	       vector<vector<pair<RevEngPoint*,int> > >& branches,
+	       vector<vector<RevEngPoint*> >& bd_pts,
+	       RevEngPoint*& pnt)
+{
+  vector<pair<RevEngPoint*,int> > cand;
+  for (size_t ki=0; ki<branches[curr].size(); ++ki)
+    {
+      size_t kj;
+      for (kj=0; kj<prev.size(); ++kj)
+	if (branches[curr][ki].second == prev[kj])
+	  break;
+      if (kj < prev.size())
+	continue;
+      for (kj=0; kj<cand.size(); ++kj)
+	if (cand[kj].first == branches[curr][ki].first &&
+	    cand[kj].second == branches[curr][ki].second)
+	  break;
+      if (kj == cand.size())
+	cand.push_back(branches[curr][ki]);
+    }
+
+  // Sort candidates
+  for (size_t ki=0; ki<cand.size(); ++ki)
+    for (size_t kj=ki+1; kj<cand.size(); ++kj)
+      if (cand[kj].second < cand[ki].second)
+	std::swap(cand[kj], cand[ki]);
+
+  // Select one candidate for each adjacent curve
+  vector<double> dist;
+  for (size_t ki=0; ki<cand.size(); )
+    {
+      size_t kj;
+      for (kj=ki+1;
+	   kj<cand.size() && cand[kj].second == cand[ki].second; ++kj);
+
+      int kr = cand[ki].second;
+      double mindist = std::numeric_limits<double>::max();
+      int ix = -1;
+      if (kr >= 0)
+	{
+	  for (size_t kh=ki; kh<kj; ++kh)
+	    {
+	      for (size_t kv=0; kv<branches[kr].size(); ++kv)
+		{
+		  if (branches[kr][kv].second != curr)
+		    continue;
+		  double dd = cand[kh].first->pntDist(branches[kr][kv].first);
+		  if (dd < mindist)
+		    {
+		      mindist = dd;
+		      ix = (int)kh;
+		    }
+		}
+	    }
+	}
+      for (int ka=(int)kj-1; ka>=(int)ki; --ka)
+	{
+	  if (ka != ix)
+	    cand.erase(cand.begin()+ka);
+	}
+      if (ix >= 0)
+	{
+	  dist.push_back(mindist);
+	  ++ki;
+	}
+    }
+
+  if (cand.size() == 0)
+    return -1;
+  
+  // Select adjacent branch
+  int ix = 0;
+  double mindist = dist[0];
+  double eps = 1.0e-9;
+  for (size_t ki=1; ki<cand.size(); ++ki)
+    {
+      if (fabs(dist[ki] - mindist) < eps)
+	{
+	  int num1 = (int)bd_pts[ix].size();
+	  int num2 = (int)bd_pts[ki].size();
+	  if (num2 > num1)
+	    {
+	      ix = (int)ki;
+	      mindist= dist[ki];
+	    }
+	}
+      else if (dist[ki] < mindist)
+	{
+	  ix = (int)ki;
+	  mindist= dist[ki];
+	}
+    }
+  pnt = cand[ix].first;
+  return cand[ix].second;
+}
+
+void splitAtSeam(shared_ptr<ParamSurface>& surf, shared_ptr<ParamCurve>& cv,
+		 double tol, double angtol, double maxlim,
+		 vector<shared_ptr<ParamCurve> >& subcvs)
+{
+  vector<Point> pos;
+  vector<Point> norm;
+  vector<Point> axis;
+  vector<int> type;  // 1=cylinder, 2=cone, 3=torus big, 4=torus small, 5=sphere
+  shared_ptr<ElementarySurface> elemsf =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+  double radius = elemsf->radius(0,0);
+  RectDomain dom = surf->containingDomain();
+  if (surf->instanceType() == Class_Cylinder ||
+      surf->instanceType() == Class_Cone ||
+      surf->instanceType() == Class_Sphere)
+    {
+      vector<Point> seam_pt(3);
+      surf->point(seam_pt, dom.umin(), 0.5*(dom.vmin()+dom.vmax()), 1);
+      pos.push_back(seam_pt[0]);
+      seam_pt[1].normalize();
+      norm.push_back(seam_pt[1]);
+      axis.push_back(elemsf->direction());
+      type.push_back((surf->instanceType() == Class_Cylinder) ? 1 :
+		     ((surf->instanceType() == Class_Cone) ? 2 : 3));
+    }
+  else if (surf->instanceType() == Class_Torus)
+    {
+      shared_ptr<Torus> tor = dynamic_pointer_cast<Torus,ParamSurface>(surf);
+      Point centre = tor->location();
+      Point dir = tor->direction();
+      vector<Point> seam_pt(3);
+      surf->point(seam_pt, dom.umin(), dom.vmin(), 1);
+      seam_pt[1].normalize();
+
+      pos.push_back(centre);
+      norm.push_back(dir);
+      axis.push_back(seam_pt[1]);
+      type.push_back(3);
+
+      pos.push_back(seam_pt[0]);
+      norm.push_back(seam_pt[1]);
+      axis.push_back(dir);
+      type.push_back(4);
+    }
+
+  double eps = std::min(tol, 1.0e-5);
+  vector<double> splitpar;
+  for (size_t ki=0; ki<pos.size(); ++ki)
+    {
+      vector<double> intpar;
+      vector<pair<double,double> > intcvs;
+      intersectCurvePlane(cv.get(), pos[ki], norm[ki], eps,
+			  intpar, intcvs);
+      for (size_t kr=0; kr<intcvs.size(); ++kr)
+	{
+	  intpar.push_back(intcvs[kr].first);
+	  intpar.push_back(intcvs[kr].second);
+	}
+      
+      for (size_t kr=0; kr<intpar.size(); ++kr)
+	{
+	  // Check if the intersection is at the seam
+	  Point pt = cv->point(intpar[kr]);
+	  Point dir = axis[ki];
+	  double rad = radius;
+	  if (type[ki] == 2)
+	    {
+	      shared_ptr<Cone> cone = dynamic_pointer_cast<Cone, ParamSurface>(surf);
+	      double upar, vpar, dist;
+	      Point close;
+	      cone->closestPoint(pt, upar, vpar, close, dist, eps);
+	      rad = cone->radius(vpar, 0.0);
+	      shared_ptr<Line> line = cone->getLine(upar);
+	      dir = line->getDirection();
+	    }
+	  double dd = pt.dist(pos[ki]);
+	  double dd2 = fabs((pt - pos[ki])*dir);
+	  double dd3 = (pt - pos[ki])*norm[ki];
+	  if (dd-dd2 < rad && dd3 < tol)
+	    splitpar.push_back(intpar[kr]);  // Will need to consider for
+	  // surfaces different from cylinder
+	}
+    }
+  if (splitpar.size() > 1)
+    {
+      std::sort(splitpar.begin(), splitpar.end());
+      for (int ka=(int)splitpar.size()-1; ka>0; --ka)
+	if (splitpar[ka] - splitpar[ka-1] < eps)
+	  {
+	    splitpar[ka-1] = 0.5*(splitpar[ka-1] + splitpar[ka]);
+	    splitpar.erase(splitpar.begin()+ka);
+	  }
+    }
+  
+  shared_ptr<ParamCurve> tmpcv = cv;
+  for (size_t ki=0; ki<splitpar.size(); ++ki)
+    {
+      vector<shared_ptr<ParamCurve> > split = tmpcv->split(splitpar[ki]);
+      subcvs.push_back(split[0]);
+      tmpcv = split[1];
+    }
+  subcvs.push_back(tmpcv);
+  int stop_all = 1;
+      
+  // shared_ptr<ElementarySurface> elemsf =
+  //   dynamic_pointer_cast<ElementarySurface, ParamSurface>(surf);
+  // if (elemsf.get())
+  //   {
+  //     bool close_u, close_v;
+  //     elemsf->isClosed(close_u, close_v);
+  //     vector<shared_ptr<ParamCurve> > seam_cvs;
+  //     vector<int> dir;
+  //     if (close_u)
+  // 	{
+  // 	  vector<shared_ptr<ParamCurve> > tmp_cvs =
+  // 	    elemsf->constParamCurves(dom.umin(), false);
+  // 	  seam_cvs.push_back(tmp_cvs[0]);
+  // 	  dir.push_back(1);
+  // 	}
+  //     if (close_v)
+  // 	{
+  // 	  vector<shared_ptr<ParamCurve> > tmp_cvs =
+  // 	    elemsf->constParamCurves(dom.vmin(), true);
+  // 	  seam_cvs.push_back(tmp_cvs[0]);
+  // 	  dir.push_back(2);
+  // 	}
+
+  //     double eps = std::max(1.0e-4, 0.001*(cv->endparam()-cv->startparam()));
+  //     subcvs.push_back(cv);
+  //     for (size_t kj=0; kj<subcvs.size(); )
+  // 	{
+  // 	  bool atseam = false;
+  // 	  for (size_t ki=0; ki<seam_cvs.size(); ++ki)
+  // 	    {
+  // 	      double par1, par2, dist;
+  // 	      Point close1, close2;
+  // 	      ClosestPoint::closestPtCurves(subcvs[kj].get(), seam_cvs[ki].get(),
+  // 					    par1, par2, dist, close1, close2);
+  // 	      if (dist > maxlim)
+  // 		continue;
+  // 	      if (par1 < subcvs[kj]->startparam()+eps ||
+  // 		  par1 > subcvs[kj]->endparam()-eps)
+  // 		continue;
+	      
+  // 	      // Check angle with surface normal
+  // 	      double sf_u = (dir[ki] == 1) ? dom.umin() : par2;
+  // 	      double sf_v = (dir[ki] == 1) ? par2 : dom.vmin();
+  // 	      Point norm;
+  // 	      surf->normal(norm, sf_u, sf_v);
+  // 	      Point vec = close2 - close1;
+  // 	      double ang = norm.angle(vec);
+  // 	      ang = std::min(ang, M_PI-ang);
+  // 	      if (ang < angtol)
+  // 		{
+  // 		  vector<shared_ptr<ParamCurve> > split =
+  // 		    subcvs[kj]->split(par1);
+  // 		  subcvs[kj] = split[0];
+  // 		  subcvs.insert(subcvs.end(), split.begin()+1, split.end());
+  // 		  atseam = true;
+  // 		  break;
+  // 		}
+  // 	    }
+  // 	  if (!atseam)
+  // 	    ++kj;
+  // 	}
+  //   }
+  // else
+  //   subcvs.push_back(cv);
+}
+
+      
+void RevEngRegion::extendBoundaries(double mean_edge_len, int min_point_reg,
+				    double tol, double angtol, Point mainaxis[3])
+//===========================================================================
+{
+
+#ifdef DEBUG_ADJUST
+  std::ofstream of1("curr_regions_adjust.g2");
+  std::ofstream of11("par_cvs_in.g2");
+  writeRegionPoints(of1);
+  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+    {
+      shared_ptr<ParamCurve> cv = trim_edgs_[ki]->geomCurve();
+      shared_ptr<CurveOnSurface> sfcv =
+	dynamic_pointer_cast<CurveOnSurface,ParamCurve>(cv);
+      if (sfcv.get())
+	{
+	  sfcv->spaceCurve()->writeStandardHeader(of1);
+	  sfcv->spaceCurve()->write(of1);
+	  if (sfcv->hasParameterCurve())
+	    SplineDebugUtils::writeSpaceParamCurve(sfcv->parameterCurve(),of11);
+	}
+    }
+#endif
+  
+  int min_nmb_adj = min_point_reg/10;
+  size_t min_bd = min_point_reg/50;
+  double min_len = 20.0*mean_edge_len;
+#ifdef DEBUG_ADJUST
+  std::ofstream of5("all_adj_adjust.g2");
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      if (commonRevEdge(*it))
+	continue;
+
+      if (commonTrimEdge(*it))
+	continue;
+
+      if ((*it)->hasBlendEdge())
+	continue;   // Trimming curves should exist already
+      // if ((*it)->numPoints() < min_nmb)
+      // 	continue;
+      (*it)->writeRegionPoints(of5);
+    }
+#endif
+
+  vector<vector<RevEngPoint*> > bd_pts1; //, bd_pts2;
+  vector<RevEngRegion*> adj_bd;
+  vector<pair<RevEngPoint*,RevEngPoint*> > end_pts;
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      if (commonRevEdge(*it))
+	continue;
+
+      if (commonTrimEdge(*it))
+	continue;
+
+      if ((*it)->hasBlendEdge())
+	continue;   // Trimming curves should exist already
+      
+      // The boundary towards adjacent regions with a surface should preferably be
+      // handled by other tools, but are currently included
+      // if ((*it)->hasSurface())
+      // 	continue;
+      // if ((*it)->numPoints() < min_nmb)
+      // 	continue;
+
+// #ifdef DEBUG_ADJUST
+//       std::ofstream of2("adj_regions_adjust.g2");
+//       (*it)->writeRegionPoints(of2);
+// #endif
+      
+      // Get boundary points
+      // Dismiss boundary points that are too close to existing trimming curves
+      vector<RevEngPoint*> adj_pts1 = extractNextToAdjacent(*it);
+      int nmb_lim = (int)adj_pts1.size()/10;
+      int nmb_in = 0;
+      for (size_t kj=0; kj<adj_pts1.size(); ++kj)
+	{
+	  Vector3D xyz = adj_pts1[kj]->getPoint();
+	  Point pnt(xyz[0], xyz[1], xyz[2]);
+	  for (size_t ki=0; ki<trim_edgs_.size(); ++ki)
+	    {
+	      shared_ptr<ParamCurve> cv = trim_edgs_[ki]->geomCurve();
+	      double tpar, dist;
+	      Point close;
+	      cv->closestPoint(pnt, cv->startparam(), cv->endparam(), tpar, close, dist);
+	      if (dist <= tol)
+		{
+		  nmb_in++;
+		  break;
+		}
+	    }
+	}
+      
+      //vector<RevEngPoint*> adj_pts2 = (*it)->extractNextToAdjacent(this);
+      if (nmb_in < nmb_lim)
+	{
+	  RevEngPoint *end1, *end2;
+	  RevEngPoint *dummy = 0;
+	  vector<RevEngPoint*> tmp_pts;
+	  for (size_t kj=0; kj<adj_pts1.size(); ++kj)
+	    if (adj_pts1[kj]->getSurfaceDist() <= tol)
+	      tmp_pts.push_back(adj_pts1[kj]);
+	  bd_pts1.push_back(tmp_pts); //adj_pts1);
+	  adj_bd.push_back(*it);
+	  if (adj_pts1.size() > 0)
+	    {
+	      endPoints(adj_pts1, end1, end2);
+	      end_pts.push_back(std::make_pair(end1, end2));
+	    }
+	  else
+	    end_pts.push_back(std::make_pair(dummy, dummy));
+	}
+      //bd_pts2.push_back(adj_pts2);
+    }
+      // if (adj_pts1.size() < min_bd || adj_pts2.size() < min_bd)
+      // 	continue;
+
+  
+  vector<vector<pair<RevEngPoint*,int> > > branches;
+  getBranches(bd_pts1, branches);
+  
+#ifdef DEBUG_ADJUST
+  std::ofstream of2("bd1.g2");
+  std::ofstream of4("end1.g2");
+  for (size_t kj=0; kj<bd_pts1.size(); ++kj)
+    if (bd_pts1[kj].size() > 0)
+      {
+	of4 << "400 1 0 4 255 0 0 255" << std::endl;
+	of4 << "2" << std::endl;
+	of4 << end_pts[kj].first->getPoint() << std::endl;
+	of4 << end_pts[kj].second->getPoint() << std::endl;
+	of2 << "400 1 0 4 0 255 0 255" << std::endl;
+	of2 << bd_pts1[kj].size() << std::endl;
+	for (size_t ki=0; ki<bd_pts1[kj].size(); ++ki)
+	  of2 << bd_pts1[kj][ki]->getPoint() << std::endl;
+      }
+  
+  // std::ofstream of3("bd2.g2");
+  // for (size_t kj=0; kj<bd_pts2.size(); ++kj)
+  //   if (bd_pts2[kj].size() > 0)
+  //     {
+  // 	of3 << "400 1 0 4 0 255 0 255" << std::endl;
+  // 	of3 << bd_pts2[kj].size() << std::endl;
+  // 	for (size_t ki=0; ki<bd_pts2[kj].size(); ++ki)
+  // 	  of3 << bd_pts2[kj][ki]->getPoint() << std::endl;
+  //     }
+
+  std::ofstream of6("bd_branches.g2");
+  for (size_t ki=0; ki<branches.size(); ++ki)
+    if (branches[ki].size() > 0 && branches[ki][0].first)
+      {
+	of6 << "400 1 0 4 255 0 0 255" << std::endl;
+	of6 << branches[ki].size() << std::endl;
+	for (size_t kj=0; kj<branches[ki].size(); ++kj)
+	  of6 << branches[ki][kj].first->getPoint() << std::endl;
+      }
+#endif
+
+  // Sort sequences and remember branch points
+  vector<vector<int> > ixs;
+  vector<vector<RevEngPoint*> > joints;
+  vector<int> num_bd;
+  for (size_t ki=0; ki<branches.size(); ++ki)
+    {
+      size_t kr,kh;
+      for (kr=0; kr<ixs.size(); ++kr)
+	{
+	  for (kh=0; kh<ixs[kr].size(); ++kh)
+	    {
+	      if (ixs[kr][kh] == (int)ki)
+		break;
+	    }
+	  if (kh < ixs[kr].size())
+	    break;
+	}
+      if (kr < ixs.size())
+	continue;
+
+      if (branches[ki].size() == 0)
+	continue;
+      
+      vector<int> ixs0;
+      vector<RevEngPoint*> joints0;
+      ixs0.push_back((int)ki);
+      int curr=(int)ki, next=-1;
+      RevEngPoint *pt;
+      int num0 = 0;
+      next = getNextSeq(ixs0, curr, branches, bd_pts1, pt);
+      while (next >= 0)
+	{
+	  ixs0.push_back(next);
+	  joints0.push_back(pt);
+	  num0 += (int)branches[curr].size();
+	  curr = next;
+	  next = getNextSeq(ixs0, curr, branches, bd_pts1, pt);
+	}
+      if (ixs0.size() > 1)
+	{
+	  curr = ixs0[0];
+	  next = getNextSeq(ixs0, curr, branches, bd_pts1, pt);
+	  while (next >= 0)
+	    {
+	      ixs0.insert(ixs0.begin(), next);
+	      joints0.insert(joints0.begin(), pt);
+	      num0 += (int)branches[curr].size();
+	      curr = next;
+	      next = getNextSeq(ixs0, curr, branches, bd_pts1, pt);
+	    }
+	}
+      ixs.push_back(ixs0);
+      joints.push_back(joints0);
+      num_bd.push_back(num0);
+    }
+
+  for (size_t ki=0; ki<ixs.size(); ++ki)
+    for (size_t kj=ki+1; kj<ixs.size(); ++kj)
+      if (num_bd[kj] > num_bd[ki])
+	{
+	  std::swap(ixs[ki], ixs[kj]);
+	  std::swap(joints[ki], joints[kj]);
+	  std::swap(num_bd[ki], num_bd[kj]);
+	}
+  
+#ifdef DEBUG_ADJUST
+  std::ofstream of7("joined_seqs.g2");
+  for (size_t ki=0; ki<ixs.size(); ++ki)
+    {
+      int num=0;
+      for (size_t kj=0; kj<ixs[ki].size(); ++kj)
+	num += (int)bd_pts1[ixs[ki][kj]].size();
+
+      of7 << "400 1 0 0" << std::endl;
+      of7 << num << std::endl;
+      for (size_t kj=0; kj<ixs[ki].size(); ++kj)
+	for (size_t kr=0; kr<bd_pts1[ixs[ki][kj]].size(); ++kr)
+	  of7 << bd_pts1[ixs[ki][kj]][kr]->getPoint() << std::endl;
+    }
+#endif
+
+  // Check if the points can be approximated by a circle or a line
+  // First collect seqences
+  vector<bool> done(ixs.size(), false);
+  vector<vector<size_t> > all_bd_ixs;
+  vector<int> all_num;
+  vector<vector<Point> > all_points;
+  vector<int> all_adj_num;
+  vector<Point> all_norm;
+  for (size_t ki=0; ki<ixs.size(); ++ki)
+    {
+      if (done[ki])
+	continue;
+
+      // Collect points
+      vector<size_t> bd_ixs;
+      size_t kj=ki;
+      int num = 0;
+      while (kj < ixs.size())
+	{
+	  done[kj] = true;
+	  for (size_t kr=0; kr<ixs[kj].size(); ++kr)
+	    {
+	      size_t kh;
+	      for (kh=0; kh<bd_ixs.size(); ++kh)
+		if (bd_ixs[kh] == ixs[kj][kr])
+		  break;
+	      if (kh == bd_ixs.size())
+		{
+		  bd_ixs.push_back(ixs[kj][kr]);
+		  num += (int)bd_pts1[ixs[kj][kr]].size();
+		}
+	    }
+	  
+	  for (++kj; kj<ixs.size(); ++kj)
+	    {
+	      size_t kr;
+	      for (kr=0; kr<ixs[kj].size(); ++kr)
+		{
+		  size_t kh;
+		  for (kh=0; kh<bd_ixs.size(); ++kh)
+		    if (bd_ixs[kh] == ixs[kj][kr])
+		      break;
+		  if (kh < bd_ixs.size())
+		    break;
+		}
+	      if (kr < ixs[kj].size())
+		break;
+	    }
+	}
+
+      vector<Point> points;
+      points.reserve(num);
+      Point norm(0.0, 0.0, 0.0);
+      double fac = 1.0/(double)num;
+      int num_adj = 0;
+      for (size_t kr=0; kr<bd_ixs.size(); ++kr)
+	{
+	  for (size_t kh=0; kh<bd_pts1[bd_ixs[kr]].size(); ++kh)
+	    {
+	      Vector3D xyz = bd_pts1[bd_ixs[kr]][kh]->getPoint();
+	      points.push_back(Point(xyz[0], xyz[1], xyz[2]));
+	      Point norm0 = bd_pts1[bd_ixs[kr]][kh]->getTriangNormal();
+	      norm += fac*norm0;
+	    }
+	  num_adj += adj_bd[bd_ixs[kr]]->numPoints();
+	}
+      all_bd_ixs.push_back(bd_ixs);
+      all_num.push_back(num);
+      all_points.push_back(points);
+      all_norm.push_back(norm);
+      all_adj_num.push_back(num_adj);
+    }
+
+  int num_lim = 10;
+  vector<shared_ptr<ElementaryCurve> > elemcv(2*all_points.size());
+  for (size_t ki=0; ki<all_points.size(); ++ki)
+  {
+    if (all_points[ki].size() < num_lim)
+      continue;
+    
+    // Compute circle
+    Point pos0, axis, Cx, Cy;
+    RevEngUtils::computePlane(all_points[ki], all_norm[ki], mainaxis, 
+			      pos0, axis, Cx, Cy);
+
+    Point centre;
+    double radius;
+    try {
+      RevEngUtils::computeCircPosRadius(all_points[ki], axis, Cx, Cy,
+					centre, radius);
+    }
+    catch (...)
+      {
+	continue;
+      }
+
+    shared_ptr<Circle> circ(new Circle(radius, centre, axis, Cx));
+    elemcv[2*ki] = circ;
+
+    // Compute line
+    Point mid, dir;
+    RevEngUtils::computeLine(all_points[ki], mid, dir);
+    shared_ptr<Line> line(new Line(mid, dir));
+    elemcv[2*ki+1] = line;
+    
+    int stop_circ = 1;
+  }
+#ifdef DEBUG_ADJUST
+  std::ofstream of8("circle_line.g2");
+  for (size_t ki=0; ki<elemcv.size(); ++ki)
+    {
+      if (elemcv[ki].get())
+	{
+	  elemcv[ki]->writeStandardHeader(of8);
+	  elemcv[ki]->write(of8);
+	}
+    }
+#endif
+  
+  // Check distance
+  vector<double> maxdist(all_points.size(), std::numeric_limits<double>::max());
+  vector<double> avdist(all_points.size(), std::numeric_limits<double>::max());
+  vector<int> num_inside(all_points.size(), 0);
+  vector<vector<double> > param(all_points.size());
+  vector<vector<double> > distance(all_points.size());
+  vector<int> match(all_points.size(), -1);
+
+  double fac1 = 0.9;
+  for (size_t ki=0; ki<all_points.size(); ++ki)
+  {
+    if (all_points[ki].size() < num_lim)
+      continue;
+    for (size_t kj=0; kj<elemcv.size(); ++kj)
+      {
+	if (!elemcv[kj].get())
+	  continue;
+	double maxd, avd;
+	int in;
+	vector<double> parvals, dist;
+	RevEngUtils::distToCurve(all_points[ki], elemcv[kj], tol, maxd,
+				 avd, in, parvals, dist);
+	if ((avd < avdist[ki] &&
+	     ((double)in > fac1*(double)num_inside[ki] || num_inside[ki] == 0)) ||
+	    (in > num_inside[ki] && fac1*avd < avdist[ki]) )
+	  {
+	    maxdist[ki] = maxd;
+	    avdist[ki] = avd;
+	    num_inside[ki] = in;
+	    param[ki] = parvals;
+	    distance[ki] = dist;
+	    match[ki] = (int)kj;
+	  }
+      }
+    int stop_dist = 1;
+  }
+
+#ifdef DEBUG_ADJUST
+  std::ofstream of9("spline.g2");
+#endif
+
+  shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+  double smoothwgt = 0.5;
+  int in=6, ik=4;
+  int maxiter = 2;
+#ifdef DEBUG_ADJUST
+  std::ofstream of10("proj_cvs.g2");
+  surf->writeStandardHeader(of10);
+  surf->write(of10);
+#endif
+  for (size_t kj=0; kj<elemcv.size(); ++kj)
+    {
+      if (!elemcv[kj].get())
+	continue;
+      
+      // Collect points associated to this curve
+      vector<int> pt_ix;
+      for (size_t ki=0; ki<match.size(); ++ki)
+	if (match[ki] == (int)kj)
+	  pt_ix.push_back((int)ki);
+
+      if (pt_ix.size() == 0)
+	continue;
+
+      vector<Point> pts;
+      vector<double> pts2;
+      vector<double> par;
+      int curr_adj_num = 0;
+      for (size_t ki=0; ki<pt_ix.size(); ++ki)
+	{
+	  pts.insert(pts.end(), all_points[pt_ix[ki]].begin(),
+		     all_points[pt_ix[ki]].end());
+	  par.insert(par.end(), param[pt_ix[ki]].begin(), param[pt_ix[ki]].end());
+	  curr_adj_num += all_adj_num[pt_ix[ki]];
+	}
+
+      // Check significance of current boundary points
+      if ((int)pts.size() < min_bd || curr_adj_num < min_nmb_adj)
+	continue;
+      
+      for (size_t ki=0; ki<par.size(); ++ki)
+	for (size_t kr=ki+1; kr<par.size(); ++kr)
+	  if (par[kr] < par[ki])
+	    {
+	      std::swap(par[kr], par[ki]);
+	      std::swap(pts[kr], pts[ki]);
+	    }
+
+      bool close = false;
+      if (kj%2 == 0)
+	{
+	  // Circular base curve. Check for internal seam
+	  double closefac = 0.1*M_PI;
+	  double avdd = 2*M_PI/(double)(par.size()-1);
+	  double ddlim = 20.0*avdd;
+	  double maxdd = 0;
+	  size_t max_ix = 0;
+	  for (size_t ki=1; ki<par.size(); ++ki)
+	    if (par[ki] - par[ki-1] > maxdd)
+	      {
+		maxdd = par[ki] - par[ki-1];
+		max_ix = ki;
+	      }
+	    if (max_ix > 0 && maxdd > ddlim)
+	      {
+		size_t nn = par.size()-max_ix;
+		par.insert(par.end(), par.begin(), par.begin()+max_ix);
+		pts.insert(pts.end(), pts.begin(), pts.begin()+max_ix);
+		par.erase(par.begin(), par.begin()+max_ix);
+		pts.erase(pts.begin(), pts.begin()+max_ix);
+		for (size_t kr=nn; kr<par.size(); ++kr)
+		  par[kr] += 2*M_PI;
+		
+	      }
+
+	  if (par[par.size()-1] - par[0] > 2*M_PI-closefac)
+	    close = true;
+ 	}
+
+
+      double tmin = par[0];
+      double tmax = par[par.size()-1];
+      for (size_t ki=1; ki<par.size(); ++ki)
+	{
+	  double dd = pts[ki-1].dist(pts[ki]);
+	  par[ki] = par[ki-1] + dd;
+	}
+
+      if (close)
+	{
+	  size_t num = par.size();
+	  pts.push_back(pts[0]);  // Repeat point at seam
+	  par.push_back(par[num-1] + pts[num-1].dist(pts[num]));
+	}
+
+
+      BoundingBox ptbb(3);
+      for (size_t ki=0; ki<pts.size(); ++ki)
+	{
+	  ptbb.addUnionWith(pts[ki]);
+	  pts2.insert(pts2.end(), pts[ki].begin(), pts[ki].end());
+	}
+      
+      
+      ApproxCurve approx(pts2, par, 3, tol, in, ik);
+      approx.setSmooth(smoothwgt);
+
+      double maxspl, avspl;
+      shared_ptr<SplineCurve> cv = approx.getApproxCurve(maxspl, avspl, maxiter);
+      BoundingBox cvbb = cv->boundingBox();
+#ifdef DEBUG_ADJUST
+      cv->writeStandardHeader(of9);
+      cv->write(of9);
+#endif
+      shared_ptr<ParamCurve> selcv = cv;
+      double maxlim = maxspl;
+      double avelem = avdist[pt_ix[0]];
+      int inelem = num_inside[pt_ix[0]];
+      double maxelem = maxdist[pt_ix[0]];
+      if (pt_ix.size() > 1)
+	{
+	  size_t num_pt = par.size();
+	  avelem = 0;
+	  inelem = 0;
+	  for (size_t kr=0; kr<pt_ix.size(); ++kr)
+	    {
+	      avelem += (double)all_points[pt_ix[kr]].size()*avdist[pt_ix[kr]]/
+		(double)num_pt;
+	      inelem += num_inside[pt_ix[kr]];
+	      maxelem = std::max(maxelem, maxdist[pt_ix[kr]]);
+	    }
+	}
+
+      double bbfac = 5.0;
+      if (avelem < avspl || (avelem <= tol && 2*inelem > (int)par.size()) ||
+	  cvbb.low().dist(cvbb.high()) > bbfac*ptbb.low().dist(ptbb.high()))
+	{
+	  if (!close)
+	    {
+	      if (tmin < -2*M_PI)
+		{
+		  tmin += 2*M_PI;
+		  tmax += 2*M_PI;
+		}
+	      if (tmax > 2*M_PI)
+		{
+		  tmin -= 2*M_PI;
+		  tmax -= 2*M_PI;
+		}
+	    }
+	    elemcv[kj]->setParamBounds(tmin, tmax);
+	  selcv = elemcv[kj];
+	  maxlim = maxelem;
+	}
+
+      // Check if the curve crosses a seam
+      vector<shared_ptr<ParamCurve> > subcvs;
+      splitAtSeam(surf, selcv, tol, angtol, maxlim, subcvs);
+      
+      vector<shared_ptr<CurveOnSurface> > trim_cv(subcvs.size());
+      for (size_t kr=0; kr<subcvs.size(); ++kr)
+	{
+	  shared_ptr<SplineCurve> proj_cv, par_cv;
+	  CurveCreators::projectCurve(subcvs[kr], surf, tol, proj_cv, 
+				      par_cv);
+	  trim_cv[kr] = shared_ptr<CurveOnSurface>(new CurveOnSurface(surf, proj_cv,
+								      false));
+	  trim_cv[kr]->ensureParCrvExistence(tol);
+#ifdef DEBUG_ADJUST
+	  proj_cv->writeStandardHeader(of10);
+	  proj_cv->write(of10);
+	  shared_ptr<ParamCurve> tmp_par = trim_cv[kr]->parameterCurve();
+	  if (tmp_par.get())
+	    SplineDebugUtils::writeSpaceParamCurve(tmp_par, of11);
+#endif
+	}
+
+      for (size_t ki=0; ki<trim_cv.size(); ++ki)
+	{
+	  double len = trim_cv[ki]->estimatedCurveLength();
+	  if (len < min_len)
+	    continue;
+	  shared_ptr<ftEdge> edg(new ftEdge(associated_sf_[0], trim_cv[ki],
+					    trim_cv[ki]->startparam(),
+					    trim_cv[ki]->endparam()));
+	  addTrimEdge(edg);
+	}
+      int stop_spl = 1;
+    }
+
+      
+  int stop_break2 = 1;
+}
+
+//===========================================================================
+vector<RevEngPoint*>  RevEngRegion::extractBdOutPoints(shared_ptr<SplineCurve>& crv,
+						       vector<RevEngPoint*>& seq_pts,
+						       double tol)
+//===========================================================================
+{
+  vector<RevEngPoint*> left, right, upper, lower;
+  vector<RevEngPoint*> points;
+  points.insert(points.end(), seq_pts.begin(), seq_pts.end());
+  size_t seq_size = seq_pts.size();
+
+  for (size_t ki=0; ki<points.size(); ++ki)
+    points[ki]->setVisited();
+
+  double maxdist = 0.0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      if (ki == seq_size)
+	maxdist *= 2.0;
+      Vector3D xyz = points[ki]->getPoint();
+      Point pos(xyz[0], xyz[1], xyz[2]);
+      double tpar, dist;
+      Point close;
+      crv->ParamCurve::closestPoint(pos, tpar, close, dist);
+      if (dist > maxdist)
+	{
+	  if (ki < seq_size)
+	    maxdist = dist;
+	  else
+	    continue;
+	}
+      
+      if (tpar <= crv->startparam())
+	lower.push_back(points[ki]);
+      else if (tpar >= crv->endparam())
+	upper.push_back(points[ki]);
+      else
+	{
+	  vector<Point> der(2);
+	  crv->point(der, tpar, 1);
+	  Point norm = points[ki]->getLocFuncNormal();
+	  Point vec = pos - close;
+	  Point vec2 = vec.cross(der[1]);
+	  if (vec2*norm >= 0)
+	    left.push_back(points[ki]);
+	  else
+	    right.push_back(points[ki]);
+	}
+      
+      vector<ftSamplePoint*> next = points[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint *curr = dynamic_cast<RevEngPoint*>(next[kj]);
+	  RevEngRegion *adj_reg = curr->region();
+	  if (curr->visited())
+	    continue;
+	  if (adj_reg != this)
+	    continue;
+	  curr->setVisited();
+	  points.push_back(curr);
+	}
+    }
+
+  for (size_t ki=0; ki<points.size(); ++ki)
+    points[ki]->unsetVisited();
+  
+  return (left.size() < right.size()) ? left : right;
+}
+
+//===========================================================================
+vector<RevEngPoint*>  RevEngRegion::sortPtsSeq(double mean_edge_len,
+					       vector<RevEngPoint*>& seq_pts,
+					       vector<RevEngPoint*>& sub_pts)
+//===========================================================================
+{
+  vector<RevEngPoint*> sub_pts2;
+  if (sub_pts.size() < 2)
+    {
+      // Check for maximum distance between input points
+      vector<RevEngPoint*> dummy;
+      if (seq_pts.size() < 2)
+	return dummy;
+
+      double fac = 10.0;
+      size_t ix1 = 0, ix2 = 1;
+      ix2 = std::min(ix2, seq_pts.size()-1);
+      double maxlen = seq_pts[ix1]->pntDist(seq_pts[ix2]);
+      for (size_t ki=0; ki<seq_pts.size(); ++ki)
+	for (size_t kj=ki+1; kj<seq_pts.size(); ++kj)
+	  {
+	    double len = seq_pts[ki]->pntDist(seq_pts[kj]);
+	    if (len > maxlen)
+	      {
+		maxlen = len;
+		ix1 = ki;
+		ix2 = kj;
+	      }
+	  }
+
+      if (sub_pts.size() == 1)
+	{
+	  sub_pts2.push_back(sub_pts[0]);
+	  double len1 = sub_pts[0]->pntDist(seq_pts[ix1]);
+	  double len2 = sub_pts[0]->pntDist(seq_pts[ix2]);
+	  if (len1 >= len2)
+	    sub_pts2.push_back(seq_pts[ix1]);
+	  else
+	    sub_pts2.push_back(seq_pts[ix2]);
+	}
+      else
+	{
+	  sub_pts2.push_back(seq_pts[ix1]);
+	  if (maxlen > fac*mean_edge_len)
+	    sub_pts2.push_back(seq_pts[ix2]);
+	}
+    }
+  else
+    sub_pts2.insert(sub_pts2.end(), sub_pts.begin(), sub_pts.end());
+  vector<RevEngPoint*> seq_pts2(seq_pts.begin(), seq_pts.end());
+  vector<vector<RevEngPoint*> > all_seq;
+  
+  while (sub_pts2.size() > 0)
+    {
+      size_t ix1 = 0, ix2 = 1;
+      ix2 = std::min(ix2, sub_pts2.size()-1);
+      double maxlen = sub_pts2[ix1]->pntDist(sub_pts2[ix2]);
+      for (size_t ki=0; ki<sub_pts2.size(); ++ki)
+	for (size_t kj=ki+1; kj<sub_pts2.size(); ++kj)
+	  {
+	    double len = sub_pts2[ki]->pntDist(sub_pts2[kj]);
+	    if (len > maxlen)
+	      {
+		maxlen = len;
+		ix1 = ki;
+		ix2 = kj;
+	      }
+	  }
+
+      vector<RevEngPoint*> sortseq;
+      sortseq.push_back(sub_pts2[ix1]);
+      bool more_pts = true;
+      RevEngPoint *curr = sub_pts2[ix1];
+      vector<RevEngPoint*> prev_pts;
+      while (more_pts)
+	{
+	  vector<RevEngPoint*> next_pts;
+	  vector<ftSamplePoint*> next = curr->getNeighbours();
+	  for (size_t ki=0; ki<next.size(); ++ki)
+	    {
+	      size_t kj;
+	      for (kj=0; kj<next_pts.size(); ++kj)
+		if (next_pts[kj] == next[ki])
+		  break;
+	      if (kj == next_pts.size())
+		{
+		  size_t kh;
+		  for (kh=0; kh<sortseq.size(); ++kh)
+		    if (sortseq[kh] == next[ki])
+		      break;
+		  if (kh == sortseq.size())
+		    {
+		      size_t kr;
+		      for (kr=0; kr<seq_pts2.size(); ++kr)
+			if (seq_pts2[kr] == next[ki])
+			  break;
+		      if (kr < seq_pts2.size())
+			next_pts.push_back(dynamic_cast<RevEngPoint*>(next[ki]));
+		    }
+		}
+	    }
+	  for (size_t ki=0; ki<next_pts.size(); ++ki)
+	    for (size_t kj=ki+1; kj<next_pts.size(); )
+	      {
+		if (next_pts[ki] == next_pts[kj])
+		  next_pts.erase(next_pts.begin()+kj);
+		else
+		  ++kj;
+	      }
+
+	  if (next_pts.size() == 0)
+	    next_pts = prev_pts;
+	  
+	  if (next_pts.size() == 0)
+	    break;
+	  if (next_pts.size() == 1)
+	    {
+	      prev_pts.clear();
+	      sortseq.push_back(next_pts[0]);
+	    }
+	  else
+	    {
+	      double minlen = curr->pntDist(next_pts[0]);
+	      size_t ix3 = 0;
+	      for (size_t kr=1; kr<next_pts.size(); ++kr)
+		{
+		  double len = curr->pntDist(next_pts[kr]);
+		  if (len < minlen)
+		    {
+		      minlen = len;
+		      ix3 = kr;
+		    }
+		}
+	      prev_pts.clear();
+	      sortseq.push_back(next_pts[ix3]);
+	      for (size_t kr=0; kr<next_pts.size(); ++kr)
+		if (kr != ix3)
+		  prev_pts.push_back(next_pts[kr]);
+	    }
+	  curr = sortseq[sortseq.size()-1];
+	}
+
+#ifdef DEBUG_SEGMENT
+      std::ofstream of("sorted_seq.g2");
+      for (size_t ki=0; ki<sortseq.size(); ++ki)
+	{
+	  of << "400 1 0 4 0 0 0 255" << std::endl;
+	  of << "1" << std::endl;
+	  of << sortseq[ki]->getPoint() << std::endl;
+	}
+#endif
+      int stop_break = 1;
+
+      for (size_t ki=0; ki<sortseq.size(); ++ki)
+	{
+	  for (size_t kj=0; kj<sub_pts2.size(); ++kj)
+	    {
+	      if (sortseq[ki] == sub_pts2[kj])
+		{
+		  sub_pts2.erase(sub_pts2.begin()+kj);
+		  break;
+		}
+	      for (size_t kj=0; kj<seq_pts2.size(); ++kj)
+		{
+		  if (sortseq[ki] == seq_pts2[kj])
+		    {
+		      seq_pts2.erase(seq_pts2.begin()+kj);
+		      break;
+		    }
+		}
+	    }
+	}
+      all_seq.push_back(sortseq);
+    }
+
+  // Join sub sequences
+  vector<RevEngPoint*> sorted;
+  if (all_seq.size() > 0)
+    {
+      int all_size = (int)all_seq.size();
+      while (all_size > 1)
+	{
+	  double t1min = std::numeric_limits<double>::max();
+	  double t2min = std::numeric_limits<double>::max();
+	  int x1 = -1, x2 = -1;
+	  bool turn1 = false, turn2 = false;
+	  for (int kj=1; kj<all_size; ++kj)
+	    {
+	      double l1 = all_seq[0][0]->pntDist(all_seq[kj][0]);
+	      double l2 = all_seq[0][0]->pntDist(all_seq[kj][all_seq[kj].size()-1]);
+	      double l3 = all_seq[0][all_seq[0].size()-1]->pntDist(all_seq[kj][0]);
+	      double l4 = all_seq[0][all_seq[0].size()-1]->pntDist(all_seq[kj][all_seq[kj].size()-1]);
+	      if (std::min(l1, l2) < t1min)
+		{
+		  t1min = std::min(l1, l2);
+		  x1 = (int)kj;
+		  turn1 = (l1 < l2);
+		}
+	    
+	      if (std::min(l3, l4) < t2min)
+		{
+		  t2min = std::min(l3, l4);
+		  x2 = (int)kj;
+		  turn2 = (l4 < l3);
+		}
+	    }
+	  if (t1min < t2min)
+	    {
+	      if (turn1)
+		{
+		  size_t last = all_seq[x1].size()-1;
+		  for (size_t kr=0; kr<all_seq[x1].size()/2; ++kr)
+		    std::swap(all_seq[x1][kr], all_seq[x1][last-kr]);
+		}
+	      all_seq[0].insert(all_seq[0].begin(), all_seq[x1].begin(),
+				all_seq[x1].end());
+	      if (x1 < all_size-1)
+		std::swap(all_seq[x1],all_seq[all_size-1]);
+	    }
+	  else
+	    {
+	      if (turn2)
+		{
+		  size_t last = all_seq[x2].size()-1;
+		  for (size_t kr=0; kr<all_seq[x2].size()/2; ++kr)
+		    std::swap(all_seq[x2][kr], all_seq[x2][last-kr]);
+		}
+	      all_seq[0].insert(all_seq[0].end(), all_seq[x2].begin(),
+				all_seq[x2].end());
+	      if (x2 < all_size-1)
+		std::swap(all_seq[x2],all_seq[all_size-1]);
+	    }
+	  all_size--;
+	}
+    }
+
+  bool include_missing = false;
+  if (include_missing)
+    {
+      // Include missing input points
+      for (size_t ki=0; ki<seq_pts2.size(); ++ki)
+	{
+	  double minlen = seq_pts2[ki]->pntDist(all_seq[0][0]);
+	  size_t min_ix = 0;
+	  for (size_t kj=1; kj<all_seq[0].size(); ++kj)
+	    {
+	      double len = seq_pts2[ki]->pntDist(all_seq[0][kj]);
+	      if (len < minlen)
+		{
+		  min_ix = kj;
+		  minlen = len;
+		}
+	    }
+
+	  double len1 = (min_ix == 0) ? 0.0 : seq_pts2[ki]->pntDist(all_seq[0][min_ix-1]);
+	  double len2 = (min_ix == all_seq[0].size()-1) ? 0.0 :
+	    seq_pts2[ki]->pntDist(all_seq[0][min_ix+1]);
+	  size_t ix = min_ix + (len2 > len1);
+	  all_seq[0].insert(all_seq[0].begin()+ix, seq_pts2[ki]);
+    }
+    }
+  return all_seq[0];
+}
+
+//===========================================================================
+void RevEngRegion::adjustWithSurf(Point mainaxis[3], int min_pt_reg,
+				  double tol, double angtol)
+//===========================================================================
+{
+  //double eps = 1.0e-6;
+  if (associated_sf_.size() == 0)
+    return;  // No surface with which to check growt
+
+  //int sfcode;
+  //ClassType classtype = associated_sf_[0]->instanceType(sfcode);
+  shared_ptr<ParamSurface> surf = associated_sf_[0]->surface();
+  shared_ptr<BoundedSurface> bdsurf =
+    dynamic_pointer_cast<BoundedSurface,ParamSurface>(surf);
+  if (bdsurf.get())
+    surf = bdsurf->underlyingSurface();
+
+#ifdef DEBUG_ADJUST  
+  std::ofstream res1("residuals_source.txt");
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    res1 << group_points_[ki]->getPoint() << " " << group_points_[ki]->getSurfaceDist() << std::endl;
+#endif
+  
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      // Check criteria for growt
+      //int num = (*it)->numPoints();
+      // if (num > group_points_.size())
+      // 	continue;
+      
+      std::ofstream res2("residuals_adj.txt");
+      for (size_t kh=0; kh<(*it)->group_points_.size(); ++kh)
+	res2 << (*it)->group_points_[kh]->getPoint() << " " << (*it)->group_points_[kh]->getSurfaceDist() << std::endl;
+  
+#ifdef DEBUG_SEGMENT
+      int num = (*it)->numPoints();
+       std::ofstream of("curr_extend.g2");
+      of << "400 1 0 4 0 255 0 255" << std::endl;
+      of << group_points_.size() << std::endl;
+      for (size_t kh=0; kh<group_points_.size(); ++kh)
+      	of << group_points_[kh]->getPoint() << std::endl;
+      
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of << num << std::endl;
+      for (int ka=0; ka<num; ++ka)
+      	of << (*it)->getPoint(ka)->getPoint() << std::endl;
+      
+      surf->writeStandardHeader(of);
+      surf->write(of);
+#endif
+      
+      vector<RevEngPoint*> in, out;
+      vector<pair<double, double> > dist_ang;
+      vector<double> parvals;
+      double maxd, avd;
+      int num_inside, num2_inside;
+      RevEngUtils::distToSurf((*it)->pointsBegin(),
+			      (*it)->pointsEnd(), surf, tol,
+			      maxd, avd, num_inside, num2_inside, in, out,
+			      parvals, dist_ang);
+      vector<RevEngPoint*> better1, worse1;
+#ifdef DEBUG_SEGMENT
+      std::ofstream res3("residuals_adj2.txt");
+      for (size_t kh=0; kh<(*it)->group_points_.size(); ++kh)
+	res3 << (*it)->group_points_[kh]->getPoint() << " " << dist_ang[kh].first << std::endl;
+#endif
+      
+      for (size_t kh=0; kh<dist_ang.size(); ++kh)
+	{
+	  double ptdist = tol, ptang = angtol;
+	  if ((*it)->hasSurface())
+	    (*it)->group_points_[kh]->getSurfaceDist(ptdist, ptang);
+	  if (dist_ang[kh].first <= ptdist && dist_ang[kh].second < angtol) //ptang)
+	  //if (dist_ang[kh].first <= tol)
+	    better1.push_back((*it)->group_points_[kh]);
+	  else
+	    worse1.push_back((*it)->group_points_[kh]);
+	}
+#ifdef DEBUG_SEGMENT
+      std::ofstream ofc1("better_worse1.g2");
+      ofc1 << "400 1 0 4 155 50 50 255" << std::endl;
+      ofc1 << better1.size() << std::endl;
+      for (size_t kr=0; kr<better1.size(); ++kr)
+	ofc1 << better1[kr]->getPoint() << std::endl;
+      ofc1 << "400 1 0 4 50 155 50 255" << std::endl;
+      ofc1 << worse1.size() << std::endl;
+      for (size_t kr=0; kr<worse1.size(); ++kr)
+	ofc1 << worse1[kr]->getPoint() << std::endl;
+      
+      std::ofstream ofd("in_out_extend.g2");
+      ofd << "400 1 0 4 155 50 50 255" << std::endl;
+      ofd << in.size() << std::endl;
+      for (size_t kr=0; kr<in.size(); ++kr)
+	ofd << in[kr]->getPoint() << std::endl;
+      ofd << "400 1 0 4 50 155 50 255" << std::endl;
+      ofd << out.size() << std::endl;
+      for (size_t kr=0; kr<out.size(); ++kr)
+	ofd << out[kr]->getPoint() << std::endl;
+#endif
+      
+      vector<RevEngPoint*> better2, worse2;
+      if ((*it)->hasSurface())
+	{
+	  shared_ptr<ParamSurface> surf2 = (*it)->getSurface(0)->surface();
+	  double maxd2, avd2;
+	  int num_inside2, num2_inside2;
+	  vector<RevEngPoint*> in2, out2;
+	  vector<pair<double, double> > dist_ang2;
+	  vector<double> parvals2;
+	  RevEngUtils::distToSurf(pointsBegin(),
+				  pointsEnd(), surf2, tol,
+				  maxd2, avd2, num_inside2, num2_inside2, in2, out2,
+				  parvals2, dist_ang2);
+	  for (size_t kh=0; kh<dist_ang2.size(); ++kh)
+	    {
+	      double ptdist, ptang;
+	      group_points_[kh]->getSurfaceDist(ptdist, ptang);
+	      if (dist_ang2[kh].first <= ptdist && dist_ang2[kh].second < angtol) //ptang)
+		{
+		  better2.push_back(group_points_[kh]);
+		}
+	      else
+		worse2.push_back(group_points_[kh]);
+	}
+#ifdef DEBUG_SEGMENt
+	  std::ofstream ofc2("better_worse2.g2");
+	  ofc2 << "400 1 0 4 155 50 50 255" << std::endl;
+	  ofc2 << better2.size() << std::endl;
+	  for (size_t kr=0; kr<better2.size(); ++kr)
+	    ofc2 << better2[kr]->getPoint() << std::endl;
+	  ofc2 << "400 1 0 4 50 155 50 255" << std::endl;
+	  ofc2 << worse2.size() << std::endl;
+	  for (size_t kr=0; kr<worse2.size(); ++kr)
+	    ofc2 << worse2[kr]->getPoint() << std::endl;
+#endif
+	}
+
+      // Move points
+      bool move2 = false;
+      size_t b1size = 2*better1.size();
+      while (better1.size() < b1size)
+	{
+	  b1size = better1.size();
+	  for (size_t ki=0; ki<better1.size(); )
+	    {
+	      if (better1[ki]->isNeighbour(this))
+		{
+		  move2 = true;
+		  (*it)->removePoint(better1[ki]);
+		  better1[ki]->setRegion(this);
+		  better1[ki]->addMove();
+		  group_points_.push_back(better1[ki]);
+		  better1.erase(better1.begin()+ki);
+		}
+	      else
+		++ki;
+	    }
+	}
+
+      if (false)
+	{
+      size_t b2size = 2*better2.size();
+      while (better2.size() < b2size)
+	{
+	  b2size = better2.size();
+	  for (size_t ki=0; ki<better2.size(); )
+	    {
+	      if (better2[ki]->isNeighbour(*it))
+		{
+		  move2 = true;
+		  removePoint(better2[ki]);
+		  better2[ki]->setRegion(*it);
+		  better2[ki]->addMove();
+		  (*it)->addPoint(better2[ki]);
+		  better2.erase(better2.begin()+ki);
+		}
+	      else
+		++ki;
+	    }
+	}
+	}
+     
+      if ((*it)->hasSurface() && (*it)->numPoints() > 5 && move2)
+	{
+	  // Check if the surface should be updated
+	  (*it)->checkReplaceSurf(mainaxis, min_pt_reg, tol, angtol);
+	}
+    }
+  
+
+  // Check if the surface should be updated
+  checkReplaceSurf(mainaxis, min_pt_reg, tol, angtol);
+#ifdef DEBUG_SEGMENt
+  std::ofstream res4("residuals_res.txt");
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    res4 << group_points_[ki]->getPoint() << " " << group_points_[ki]->getSurfaceDist() << std::endl;
+#endif
+  int stop_break = 1;
+  
+}
+  
+//===========================================================================
+bool RevEngRegion::getCurveRestriction(vector<shared_ptr<CurveOnSurface> >& cvs,
+				       double tol, double anglim,
+				       vector<pair<double,double> >& endpars)
+//===========================================================================
+{
+  double int_tol = 1.0e-6;
+  Point corner[4];
+  corner[0] = Point(domain_[0], domain_[2]);
+  corner[1] = Point(domain_[1], domain_[2]);
+  corner[2] = Point(domain_[0], domain_[3]);
+  corner[3] = Point(domain_[1], domain_[3]);
+  
+  // Ensure parameter representation of curves
+  endpars.resize(cvs.size());
+  for (size_t ki=0; ki<cvs.size(); ++ki)
+    {
+      double start = std::numeric_limits<double>::max();
+      double end = std::numeric_limits<double>::lowest();
+      if (!(cvs[ki]->hasParameterCurve()))
+	{
+	  shared_ptr<ParamSurface> surf = cvs[ki]->underlyingSurface();
+	  if (!surf->isBounded())
+	    {
+	      double diag = bbox_.low().dist(bbox_.high());
+	      associated_sf_[0]->limitSurf(2*diag);
+	    }
+	  bool OK = cvs[ki]->ensureParCrvExistence(tol);
+	  if (!OK)
+	    {
+	      // Curve larger than surface. Get initial restriction
+	      shared_ptr<ParamSurface> surf = cvs[ki]->underlyingSurface();
+	      CurveLoop cvloop = surf->outerBoundaryLoop();
+	      vector<shared_ptr<ParamCurve> > lcvs = cvloop.getCurves();
+	      shared_ptr<ParamCurve> scurve = cvs[ki]->spaceCurve();
+	      vector<double> parvals;
+	      for (size_t kr=0; kr<lcvs.size(); ++kr)
+		{
+		  vector<pair<double,double> > intpts;
+		  intersectParamCurves(scurve.get(), lcvs[kr].get(), int_tol,
+				       intpts);
+		  for (size_t kj=0; kj<intpts.size(); ++kj)
+		    parvals.push_back(intpts[kj].first);
+		}
+	      std::sort(parvals.begin(), parvals.end());
+	      double t1 = cvs[ki]->startparam();
+	      double t2 = cvs[ki]->endparam();
+	      if (parvals.size() > 1)
+		{
+		  t1 = parvals[0];
+		  t2 = parvals[parvals.size()-1];
+		}
+	      else if (parvals.size() == 1)
+		{
+		  Point pt1 = scurve->point(t1);
+		  Point pt2 = scurve->point(t2);
+		  double u1, u2, v1, v2, d1, d2;
+		  Point cl1, cl2;
+		  surf->closestPoint(pt1, u1, v1, cl1, d1, int_tol);
+		  surf->closestPoint(pt2, u2, v2, cl2, d2, int_tol);
+		  if (d1 <= d2)
+		    t2 = parvals[0];
+		  else
+		    t1 = parvals[0];
+		}
+	      shared_ptr<CurveOnSurface> sub_cv(cvs[ki]->subCurve(t1,t2));
+	      cvs[ki] = sub_cv;
+	      OK = cvs[ki]->ensureParCrvExistence(tol);
+	      if (!OK)
+		continue;
+	    }
+	}
+      
+      start = std::min(start, cvs[ki]->startparam());
+      end = std::max(end, cvs[ki]->endparam());
+      endpars[ki] = std::make_pair(start,end);
+    }
+
+  // Special treatment of closed curve
+  double seam_dist = std::numeric_limits<double>::max();
+  if (cvs.size() == 1)
+    {
+      Point startpt = cvs[0]->ParamCurve::point(cvs[0]->startparam());
+      Point endpt = cvs[0]->ParamCurve::point(cvs[0]->endparam());
+      if (startpt.dist(endpt) <= tol)
+	{
+	  closestPoint(startpt, seam_dist);
+	}
+    }
+
+  // Restrict curve with respect to parameter domain
+  double fac = 0.1;
+  for (size_t ki=0; ki<cvs.size(); ++ki)
+    {
+      vector<double> cvparam(4);
+      shared_ptr<ParamCurve> pcurve = cvs[ki]->parameterCurve();
+      if (!pcurve.get())
+	continue;
+      BoundingBox bbp = pcurve->boundingBox();
+      double u1 = bbp.low()[0];
+      double u2 = bbp.high()[0];
+      double v1 = bbp.low()[1];
+      double v2 = bbp.high()[1];
+      double udel = fac*(domain_[1] - domain_[0]);
+      double vdel = fac*(domain_[3] - domain_[2]);
+      bool inside = true;
+      for (int ka=0; ka<4; ++ka)
+	{
+	  double dist;
+	  Point close;
+	  pcurve->closestPoint(corner[ka], pcurve->startparam(),
+			       pcurve->endparam(), cvparam[ka], close, dist);
+	  if (dist < seam_dist && (u1 < domain_[0]-udel || u2 > domain_[1]+udel ||
+				   v1 < domain_[2]-vdel || v2 > domain_[3]+vdel))
+	    inside = false;
+	}
+      std::sort(cvparam.begin(), cvparam.end());
+      if (inside)
+	{
+	  endpars[ki].first = pcurve->startparam();
+	  endpars[ki].second = pcurve->endparam();
+	}
+      else
+	{
+	  endpars[ki].first = cvparam[0];
+	  endpars[ki].second = cvparam[3];
+	}
+    }
+
+
+  return true;
+}
+
+//===========================================================================
+void RevEngRegion::checkReplaceSurf(Point mainaxis[3], int min_pt_reg,
+				    double tol, double angtol, bool always)
+//===========================================================================
+{
+  int sfcode;
+  ClassType classtype[2];
+  classtype[0] = Class_Unknown;
+  classtype[1] = associated_sf_[0]->instanceType(sfcode);
+  shared_ptr<ParamSurface> primary;
+  if (basesf_.get() && avdist_base_ < tol &&
+      num_in_base_ > (int)group_points_.size()/2)
+    {
+      primary = basesf_;
+      classtype[0] = primary->instanceType();
+    }
+
+  shared_ptr<SplineCurve> profile;
+  Point pt1, pt2;
+  bool cyllike = false;
+  for (int ka=0; ka<2; ++ka)
+    {
+      if (classtype[ka] == Class_Unknown)
+	continue;
+  
+      shared_ptr<ParamSurface> updated, updated2;
+      if (classtype[ka] == Class_SplineSurface && sfcode == 1)
+	updated = computeLinearSwept(tol, profile, pt1, pt2);
+      else
+	computeSurface(group_points_, mainaxis, tol, angtol, classtype[ka], 
+		       updated, updated2, cyllike);
+
+      shared_ptr<ParamSurface> replacesurf;
+      if (updated.get())
+	{
+	  double maxd, avd;
+	  int num_inside, num2_inside;
+	  vector<RevEngPoint*> inpt, outpt;
+	  vector<pair<double, double> > dist_ang;
+	  vector<double> parvals;
+	  RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+				  updated, tol, maxd, avd, num_inside, num2_inside,
+				  inpt, outpt, parvals, dist_ang, angtol);
+	  int sf_flag1 = defineSfFlag(0, tol, num_inside, num2_inside, avd,
+				      cyllike);
+	  if (updated2.get())
+	    {
+	      double maxd2, avd2;
+	      int num_inside2, num2_inside2;
+	      vector<RevEngPoint*> inpt2, outpt2;
+	      vector<pair<double, double> > dist_ang2;
+	      vector<double> parvals2;
+	      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+				      updated2, tol, maxd2, avd2, num_inside2,
+				      num2_inside2, inpt2, outpt2, parvals2,
+				      dist_ang2, angtol);
+	      int sf_flag2 = defineSfFlag(0, tol, num_inside2, num2_inside2, avd2,
+					  cyllike);
+	      if ((sf_flag2 < sf_flag1 ||
+		   (num_inside2 > num_inside ||
+		    (num_inside2 == num_inside && avd2 < avd))) &&
+		  (sf_flag2 < surfflag_ ||
+		    (((num_inside2 > num_inside_ ||
+		       (num_inside2 == num_inside_ && avd2 < avdist_))
+		      && avd2 < tol)) || always))
+		{
+		  replacesurf = updated2;
+		  for (size_t kh=0; kh<group_points_.size(); ++kh)
+		    {
+		      group_points_[kh]->setPar(Vector2D(parvals2[2*kh],parvals2[2*kh+1]));
+		      group_points_[kh]->setSurfaceDist(dist_ang2[kh].first, dist_ang2[kh].second);
+		    }
+		  setAccuracy(maxd2, avd2, num_inside2, num2_inside2);
+		  setSurfaceFlag(sf_flag2);
+		}
+	      if (ka == 0 && num_inside2 >= num_in_base_ && avd2 < avdist_base_)
+		{
+		  setBaseSf(updated2, maxd2, avd2, num_inside2, num2_inside2);
+		}
+	      int stop_break2 = 1;
+	    }
+	  if ((!replacesurf.get()) &&
+	      (sf_flag1 < surfflag_ ||
+	       (num_inside > num_inside_ ||
+		(num_inside == num_inside_ && avd < avdist_)) || always))
+	    {
+	      replacesurf = updated;
+	      for (size_t kh=0; kh<group_points_.size(); ++kh)
+		{
+		  group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+		  group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+		}
+	      setAccuracy(maxd, avd, num_inside, num2_inside);
+	      setSurfaceFlag(sf_flag1);
+	    }
+	  if (ka == 0 && num_inside >= num_in_base_ && avd < avdist_base_)
+	    {
+	      setBaseSf(updated, maxd, avd, num_inside, num2_inside);
+	    }
+	  int stop_break = 1;
+	}
+
+      if (replacesurf.get())
+	{
+	  associated_sf_[0]->replaceSurf(replacesurf);
+	  if (!replacesurf->isBounded())
+	    {
+	      double diag = bbox_.low().dist(bbox_.high());
+	      associated_sf_[0]->limitSurf(2*diag);
+	    }
+	  
+	  surf_adaption_ = INITIAL;
+	  if (sfcode == 1 && profile.get())
+	    {
+	      // A linear swept surface
+	      associated_sf_[0]->setLinearSweepInfo(profile, pt1, pt2);
+	    }
+	  computeDomain();
+	  for (size_t kj=0; kj<rev_edges_.size(); ++kj)
+	    rev_edges_[kj]->replaceSurf(this, replacesurf, tol);
+	}
+    }
+}
+
+
+//===========================================================================
+int RevEngRegion::checkSurfaceAccuracy(vector<shared_ptr<ElementarySurface> >& sfs,
+				       double tol, double angtol, double& maxd,
+				       double& avd, int& num_in,
+				       int& num2_in, int& sf_flag)
+//===========================================================================
+{
+  vector<double> maxdist(sfs.size());
+  vector<double> avdist(sfs.size());
+  vector<int> num_inside(sfs.size());
+  vector<int> num2_inside(sfs.size());
+  vector<int> surf_flag(sfs.size());
+
+  for (size_t ki=0; ki<sfs.size(); ++ki)
+    {
+      bool cyllike = (sfs[ki]->instanceType() == Class_Cylinder ||
+		      sfs[ki]->instanceType() == Class_Cone);
+      vector<RevEngPoint*> inpt, outpt;
+      vector<pair<double, double> > dist_ang;
+      vector<double> parvals;
+      RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			      sfs[ki], tol, maxdist[ki], avdist[ki],
+			      num_inside[ki], num2_inside[ki],
+			      inpt, outpt, parvals, dist_ang, angtol);
+      surf_flag[ki] = defineSfFlag(0, tol, num_inside[ki], num2_inside[ki], 
+				   avdist[ki], cyllike);
+    }
+
+  int ix = 0;
+  double fac = 1.2;
+  for (size_t ki=1; ki<sfs.size(); ++ki)
+    {
+      if (surf_flag[ki] < surf_flag[ix] ||
+	  (surf_flag[ki] == surf_flag[ix] &&
+	   ((num2_inside[ki] >= num2_inside[ix] && avdist[ki] <= avdist[ix]) ||
+	    (double)num2_inside[ki] >= fac*(double)num2_inside[ix] ||
+	    fac*avdist[ki] <= avdist[ix])))
+	ix = (int)ki;
+    }
+  maxd = maxdist[ix];
+  avd = avdist[ix];
+  num_in = num_inside[ix];
+  num2_in = num2_inside[ix];
+  sf_flag = surf_flag[ix];
+  
+  return (sf_flag < NOT_SET) ? ix : -1;
+}
+
+//===========================================================================
+void RevEngRegion::parameterizePoints(double tol, double angtol)
+//===========================================================================
+{
+  if (!hasSurface())
+    return;
+
+    shared_ptr<ParamSurface> surf = getSurface(0)->surface();
+
+    // Compute accuracy
+    vector<RevEngPoint*> inpt, outpt;
+    vector<pair<double, double> > dist_ang;
+    double maxd, avd;
+    int num_in, num2_in;
+    vector<double> parvals;
+    RevEngUtils::distToSurf(group_points_.begin(), group_points_.end(),
+			    surf, tol, maxd, avd, num_in, num2_in,
+			    inpt, outpt, parvals, dist_ang,
+			    angtol);
+    
+    // Update info in points
+    for (size_t kh=0; kh<group_points_.size(); ++kh)
+      {
+	group_points_[kh]->setPar(Vector2D(parvals[2*kh],parvals[2*kh+1]));
+	group_points_[kh]->setSurfaceDist(dist_ang[kh].first, dist_ang[kh].second);
+      }
+  
+    updateInfo(tol, angtol);
+
+    bool cyllike = (surf->instanceType() == Class_Cylinder ||
+		    surf->instanceType() == Class_Cone);
+    int sf_flag = defineSfFlag(0, tol, num_inside_, num_inside2_,
+			       avdist_, cyllike);
+    setSurfaceFlag(sf_flag);
+}
+
+//===========================================================================
+bool RevEngRegion::isCompatible(ClassType classtype, int sfcode)
+//===========================================================================
+{
+  return true;
+  double anglim = 0.1;
+  if (classtype == Class_Plane)
+    {
+      if (planartype() || (normalcone_.angle() <= anglim &&
+			   (!normalcone_.greaterThanPi())))
+	return true;
+      else
+	return false;
+    }
+  else
+  if (classtype == Class_Cylinder)
+    {
+      return true;  // Must mike proper criteria
+    }
+    return false;  // To be extended
+}
+
+
+//===========================================================================
+void  RevEngRegion::computeSurface(vector<RevEngPoint*>& points,
+				   Point mainaxis[3], double tol,
+				   double angtol, ClassType classtype,
+				   shared_ptr<ParamSurface>& updated,
+				   shared_ptr<ParamSurface>& updated2,
+				   bool& cyllike)
+//===========================================================================
+{
+  if (classtype == Class_Plane)
+    {
+      updated = computePlane(points, avnorm_, mainaxis);
+    }
+  else if (classtype == Class_Cylinder)
+    {
+      cyllike = true;
+      updated = computeCylinder(points, tol);
+    }
+  else if (classtype == Class_Sphere)
+    {
+      Point axis;
+      if (hasSurface())
+	{
+	  shared_ptr<ElementarySurface> elem =
+	    dynamic_pointer_cast<ElementarySurface,ParamSurface>(associated_sf_[0]->surface());
+	  if (elem.get())
+	    axis = elem->direction();
+	}
+      updated = computeSphere(mainaxis, axis, points);
+    }
+  else if (classtype == Class_Cone)
+    {
+      cyllike = true;
+      Point apex;
+      updated = computeCone(points, apex);
+    }
+  else if (classtype == Class_Torus)
+    {
+      //shared_ptr<Torus> torus2;
+      vector<Point> dummy_axis;
+      updated = computeTorus(points, dummy_axis, tol, angtol);
+      //updated2 = torus2;
+    }
+  else if (classtype == Class_SplineSurface)
+    {
+      updated = updateFreeform(points, tol);
+      if (!updated.get())
+	updated = computeFreeform(points, tol);
+    }
+#ifdef DEBUG_UPDATE
+  if (updated.get())
+    {
+      std::ofstream ofn("updated.g2");
+      updated->writeStandardHeader(ofn);
+      updated->write(ofn);
+      if (updated2.get())
+	{
+	  updated2->writeStandardHeader(ofn);
+	  updated2->write(ofn);
+	}
+    }
+#endif
+}
+
+
+//===========================================================================
+void  RevEngRegion::splitCylinderRad(const Point& pos, const Point& axis,
+				     const Point& Cx, const Point& Cy,
+				     int nmb_split, vector<Point>& centr,
+				     vector<double>& rad)
+//===========================================================================
+{
+  centr.resize(nmb_split);
+  rad.resize(nmb_split);
+  shared_ptr<Line> line(new Line(pos, axis));
+  vector<double> par(group_points_.size());
+  double tmin = std::numeric_limits<double>::max();
+  double tmax = std::numeric_limits<double>::lowest();
+  double diag = bbox_.low().dist(bbox_.high());
+  double tmin0 = -diag;
+  double tmax0 = diag;
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector3D xyz = group_points_[ki]->getPoint();
+      Point curr(xyz[0], xyz[1], xyz[2]);
+      double tpar, dist;
+      Point close;
+      line->closestPoint(curr, tmin0, tmax0, tpar, close, dist);
+      par[ki] = tpar;
+      tmin = std::min(tmin, tpar);
+      tmax = std::max(tmax, tpar);
+    }
+
+  double tdel = (tmax - tmin)/(double)nmb_split;
+  vector<Point> mid(nmb_split);
+    double tpar = tmin + 0.5*tdel;
+  for (int ka=0; ka<nmb_split; ++ka, tpar+=tdel)
+    mid[ka] = line->ParamCurve::point(tpar);
+
+  vector<vector<Point> > proj_pts(nmb_split);
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      Vector3D pnt = group_points_[ki]->getPoint();
+      size_t kj;
+      for (kj=0, tpar=tmin; kj<mid.size(); ++kj, tpar+=tdel)
+	if (par[ki] >= tpar && par[ki] < tpar+tdel)
+	  break;
+      kj = std::min(kj, mid.size()-1);
+      Point curr(pnt[0], pnt[1], pnt[2]);
+      Point curr2 = curr - mid[kj];
+      curr2 -= ((curr2*axis)*axis);
+      curr2 += mid[kj];
+      proj_pts[kj].push_back(curr2);
+    }
+
+#ifdef DEBUG_SEGMENT
+  std::ofstream of("split_circ.g2");
+  for (size_t kj=0; kj<proj_pts.size(); ++kj)
+    {
+      RevEngUtils::computeCircPosRadius(proj_pts[kj], axis, Cx, Cy, centr[kj], rad[kj]);
+      shared_ptr<Circle> circ(new Circle(rad[kj], centr[kj], axis, Cx));
+      circ->writeStandardHeader(of);
+      circ->write(of);
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of << proj_pts[kj].size() << std::endl;
+      for (size_t kr=0; kr<proj_pts[kj].size(); ++kr)
+	of << proj_pts[kj][kr] << std::endl;
+    }
+#endif
+
+  
+  int stop_break = 1;
+ }
+
+
+int compare_t(const void* el1, const void* el2)
+{
+  if (((double*)el1)[0] < ((double*)el2)[0])
+    return -1;
+  else if (((double*)el1)[0] > ((double*)el2)[0])
+    return 1;
+  else
+    return 0;
+}
+
+//===========================================================================
+void  RevEngRegion::curveApprox(vector<Point>& points, double tol,
+				shared_ptr<Circle> circle, vector<double>& parval,
+				shared_ptr<SplineCurve>& curve, Point& xpos)
+//===========================================================================
+{
+  double eps = 0.001;
+  vector<double> pts;
+  vector<double> param;
+  double tmin = circle->startparam();
+  double tmax = circle->endparam();
+  double tdel = tmax - tmin;
+  double tmin2 = tmax;
+  double tmax2 = tmin;
+  vector<double> tmppts;
+  tmppts.reserve(4*points.size());
+  parval.resize(points.size());
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double tpar, dist;
+      Point close;
+      circle->closestPoint(points[ki], tmin, tmax, tpar, close, dist);
+      // if (ki > 0 && ((tpar-tmin < tmin2-tpar && tmax-tmax2 < tmin2-tmin) ||
+      // 		     (tmax-tpar < tpar-tmax2 && tmin2-tmin < tmax-tmax2)))
+      if (ki > 0 && (tpar<tmin2 || tpar>tmax2) &&
+	  std::min(fabs(tmin2-tpar+tdel),fabs(tpar+tdel-tmax2)) <
+	  std::min(fabs(tpar-tmax2),fabs(tpar-tmin2)))
+	//(fabs(tmin2-tpar+tdel) < fabs(tpar-tmax2) || fabs(tpar+tdel-tmax2) < fabs(tpar-tmax2)))
+	{
+	  if (tpar-tmin < tmax-tpar)
+	    tpar += tdel;
+	  else
+	    tpar -= tdel;
+	}
+
+      parval[ki] = tpar;
+      tmppts.push_back(tpar);
+      tmppts.insert(tmppts.end(), points[ki].begin(), points[ki].end());
+      // pts.insert(pts.end(), points[ki].begin(), points[ki].end());
+      // param.push_back(tpar);
+      tmin2 = std::min(tmin2, tpar);
+      tmax2 = std::max(tmax2, tpar);
+    }
+
+  qsort(&tmppts[0], points.size(), 4*sizeof(double), compare_t);
+  pts.resize(3*points.size());
+  param.resize(points.size());
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      param[ki] = tmppts[4*ki];
+      for (size_t ka=0; ka<3; ++ka)
+	pts[3*ki+ka] = tmppts[4*ki+ka+1];
+    }
+
+  if (tmax2 - tmin2 < 2*M_PI-eps && (tmin2 < -eps || tmax2 > 2*M_PI+eps))
+    {
+      double tpar = (tmax2 > 2*M_PI+eps) ? 0.5*(tmax2 + tmin2 - 2*M_PI)
+		      : 0.5*(tmin2 + tmax2 + 2*M_PI);
+      xpos = circle->ParamCurve::point(tpar);
+    }
+    
+  int inner = (int)(2.0*(tmax2 - tmin2)/M_PI);
+  int ik = 4;
+  int in = ik + inner;
+  // double tdel = (tmax2 - tmin2)/(double)(in - ik + 1);
+  // double et[12];
+  // for (int ka=0; ka<ik; ++ka)
+  //   {
+  //     et[ka] = tmin2;
+  //     et[in+ka] = tmax2;
+  //   }
+  // for (int ka=ik; ka<in; ++ka)
+  //   et[ka] = tmin2 + (ka-ik+1)*tdel;
+
+  double smoothwgt = 1.0e-9; //0.001;
+  ApproxCurve approx(pts, param, 3, tol, in, ik);
+  approx.setSmooth(smoothwgt);
+  int maxiter = 4; //3;
+  double maxdist, avdist;
+  curve = approx.getApproxCurve(maxdist, avdist, maxiter);
+  // vector<double> ecoef(3*in, 0.0);
+  // shared_ptr<SplineCurve> cv(new SplineCurve(in, ik, et, &ecoef[0], 3));
+
+  // SmoothCurve smooth(3);
+  // vector<int> cfn(in, 0);
+  // vector<double> wgts(param.size(), 1.0);
+  // smooth.attach(cv, &cfn[0]);
+
+  // double wgt1 = 0.0, wgt2 = 0.1, wgt3 = 0.1;
+  // double approxwgt = 1.0 - wgt1 - wgt2 - wgt3;
+  // smooth.setOptim(wgt1, wgt2, wgt3);
+  // smooth.setLeastSquares(pts, param, wgts, approxwgt);
+
+  // shared_ptr<SplineCurve> curve0;
+  // smooth.equationSolve(curve0);
+#ifdef DEBUG
+  std::ofstream of("points_and_tangents.txt");
+  of << points.size() << std::endl;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      of << pts[3*ki] << " " << pts[3*ki+1] << " ";
+      vector<Point> der(2);
+      curve->point(der, param[ki], 1);
+      of << der[1][0] << " " << der[1][1] << std::endl;
+    }
+#endif
+  int stop_break = 1;
+}
+
+//===========================================================================
+void RevEngRegion::configSplit(vector<RevEngPoint*>& points,
+			       vector<double>& param,
+			       shared_ptr<Cylinder> cyl,
+			       shared_ptr<SplineCurve> spl, double tol,
+			       vector<vector<RevEngPoint*> >& configs)
+//===========================================================================
+{
+  // Check cylinder axis
+  Point axis = cyl->direction();
+  double angtol = 0.2;
+  double inlim = 0.8;
+  int nmb_in = 0;
+  double mpi2 = 0.5*M_PI;
+  vector<Vector3D> low, high;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      Point normal = group_points_[kr]->getLocFuncNormal();
+      double ang = axis.angle(normal);
+      if (ang < mpi2)
+	low.push_back(group_points_[kr]->getPoint());
+      else
+	high.push_back(group_points_[kr]->getPoint());
+      if (fabs(ang-mpi2) <= angtol)
+	nmb_in++;
+    }
+  double in_frac = (double)nmb_in/(double)group_points_.size();
+  if (in_frac < inlim)
+    return;
+
+#ifdef DEBUG_SEGMENT
+  std::ofstream of1("low_axis.g2");
+  of1 << "400 1 0 4 100 155 0 255" << std::endl;
+  of1 << low.size() << std::endl;
+  for (size_t kr=0; kr<low.size(); ++kr)
+    of1 << low[kr] << std::endl;
+  
+  std::ofstream of2("high_axis.g2");
+  of2 << "400 1 0 4 0 155 100 255" << std::endl;
+  of2 << high.size() << std::endl;
+  for (size_t kr=0; kr<high.size(); ++kr)
+    of2 << high[kr] << std::endl;
+#endif
+  
+  // Compute all intersections between the cylinder and the spline curve
+  double eps = std::min(1.0e-6, 0.1*tol);
+  vector<double> intpar;
+  vector<pair<double,double> > int_cvs;
+  intersectCurveCylinder(spl.get(), cyl->getLocation(), cyl->getAxis(),
+			 cyl->getRadius(), eps, intpar, int_cvs);
+  for (size_t ki=0; ki<int_cvs.size(); ++ki)
+    {
+      intpar.push_back(int_cvs[ki].first);
+      intpar.push_back(int_cvs[ki].second);
+    }
+  if (intpar.size() == 0)
+    return;
+
+  // Define parameter intervals of different configurations
+  std::sort(intpar.begin(), intpar.end());
+  vector<double> delpar;
+
+  // Check startpoint, endpoint and points in the middle of intervals
+  double upar, vpar, dist;
+  Point close;
+  Point pos = spl->ParamCurve::point(spl->startparam());
+  cyl->closestPoint(pos, upar, vpar, close, dist, eps);
+  delpar.push_back(spl->startparam()-tol);
+  if (dist > tol)
+    {
+      delpar.push_back(intpar[0]);
+    }
+  for (size_t ki=1; ki<intpar.size(); ++ki)
+    {
+      double tpar = 0.5*(intpar[ki-1] + intpar[ki]);
+      pos = spl->ParamCurve::point(tpar);
+      cyl->closestPoint(pos, upar, vpar, close, dist, eps);
+      if (dist > tol)
+	delpar.push_back(intpar[ki]);
+    }
+  pos = spl->ParamCurve::point(spl->endparam());
+  cyl->closestPoint(pos, upar, vpar, close, dist, eps);
+  if (dist > tol)
+    {
+      if (delpar.size() > 0 &&
+	  intpar[intpar.size()-1] > delpar[delpar.size()-1])
+	delpar.push_back(intpar[intpar.size()-1]);
+    }
+  delpar.push_back(spl->endparam());
+
+  if (delpar.size() == 0)
+    return;
+  
+  // Divide point set according to configuration
+  configs.resize(delpar.size()-1);
+  for (size_t kj=0; kj<points.size(); ++kj)
+    {
+      for (size_t ki=1; ki<delpar.size(); ++ki)
+	if (param[kj] > delpar[ki-1] && param[kj] <= delpar[ki])
+	  {
+	    configs[ki-1].push_back(points[kj]);
+	    break;
+	  }
+    }
+  int stop_break = 1;
+}
+
+//===========================================================================
+bool  RevEngRegion::hasEdgeBetween(RevEngRegion* adj)
+//===========================================================================
+{
+  if (adj->numPoints() < (int)group_points_.size())
+    return adj->hasEdgeBetween(this);
+
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      vector<ftSamplePoint*> next = group_points_[ki]->getNeighbours();
+      for (size_t kj=0; kj<next.size(); ++kj)
+	{
+	  RevEngPoint *pt = dynamic_cast<RevEngPoint*>(next[kj]);
+	  if (!pt->isEdge(edge_class_type_))
+	    continue;
+
+	  vector<ftSamplePoint*> next2 = pt->getNeighbours();
+	  for (size_t kr=0; kr<next2.size(); ++kr)
+	    {
+	      RevEngPoint *pt2 = dynamic_cast<RevEngPoint*>(next2[kr]);
+	      if (pt2->region() == adj)
+		return true;
+	    }
+	}
+    }
+  return false;
+}
+
+//===========================================================================
+void RevEngRegion::store(std::ostream& os) const
+//===========================================================================
+{
+  os << Id_ << std::endl;
+  os << group_points_.size() << std::endl;
+for (size_t ki=0; ki<group_points_.size(); ++ki)
+  os << group_points_[ki]->getIndex() << " ";
+ os << std::endl;
+ os << classification_type_ << " " << surfflag_ << " " << surf_adaption_;
+ os << " " << frac_norm_in_ << " " << frac_norm_in2_ << std::endl;
+ os << maxdist_ << " " << avdist_ << " " << num_inside_;
+ os << " " << num_inside2_ << " " << std::endl;
+ int base = basesf_.get() ? 1 : 0;
+ os << base << std::endl;
+ if (base)
+   {
+     basesf_->writeStandardHeader(os);
+     basesf_->write(os);
+     os << maxdist_base_ << " " << avdist_base_ << " " << num_in_base_;
+     os << " " << num_in_base2_ << std::endl;
+   }
+
+ os << associated_sf_.size() << std::endl;
+ for (size_t ki=0; ki<associated_sf_.size(); ++ki)
+     os << associated_sf_[ki]->getId() << " ";
+
+ int sweep = sweep_.get() ? 1 : 0;
+ os << sweep << std::endl;
+ if (sweep_)
+   {
+     os << sweep_->type_ << std::endl;
+     if (sweep_->profile_.get())
+       {
+	 os << "1" << std::endl;
+	 sweep_->profile_->writeStandardHeader(os);
+	 sweep_->profile_->write(os);
+       }
+     else
+       os << "0" << std::endl;
+     os << sweep_->location_ << " " << sweep_->added_info_ << " ";
+     os << sweep_->radius_ << " " << sweep_->angle_ << std::endl;
+     os << sweep_->maxdist_ << " " << sweep_->avdist_ << " " << sweep_->num_in_<< std::endl;
+   }
+}
+
+//===========================================================================
+void RevEngRegion::read(std::istream& is,
+			shared_ptr<ftPointSet>& tri_sf,
+			vector<int>& associated_sf_id)
+//===========================================================================
+{
+  GoTools::init();
+  is >> Id_;
+  int num_points;
+  is >> num_points;
+  group_points_.resize(num_points);
+  int ix;
+  for (int ki=0; ki<num_points; ++ki)
+    {
+      is >> ix;
+      RevEngPoint* pt = dynamic_cast<RevEngPoint*>((*tri_sf)[ix]);
+      pt->setRegion(this);
+      group_points_[ki] = pt;
+      }
+  is >> classification_type_ >> surfflag_ >> surf_adaption_;
+  is >> frac_norm_in_ >> frac_norm_in2_;
+  is >> maxdist_ >> avdist_ >> num_inside_ >> num_inside2_;
+  int base;
+  is >> base;
+
+  if (base)
+    {
+      ObjectHeader header;
+      header.read(is);
+      shared_ptr<GeomObject> obj(Factory::createObject(header.classType()));
+      obj->read(is);
+      basesf_ = dynamic_pointer_cast<ParamSurface,GeomObject>(obj);
+      is >> maxdist_base_ >> avdist_base_ >> num_in_base_ >> num_in_base2_;
+    }
+
+  int num_sf;
+  is >> num_sf;
+  for (int ki=0; ki<num_sf; ++ki)
+    {
+      int sf_id;
+      is >> sf_id;
+      associated_sf_id.push_back(sf_id);
+    }
+  if (num_sf > 0)
+    computeDomain();
+
+  int sweep;
+  is >> sweep;
+  if (sweep)
+    {
+      int stype, snum_in, sprof;
+      double rad, ang, maxd, avd;
+      Point loc(3), added(3);
+      shared_ptr<SplineCurve> profile;
+      is >> stype;
+      is >> sprof;
+      if (sprof)
+	{
+	  ObjectHeader header;
+	  header.read(is);
+	  profile = shared_ptr<SplineCurve>(new SplineCurve());
+	  profile->read(is);
+	}
+      is >> loc >> added >> rad >> ang >> maxd >> avd >> snum_in;
+      sweep_ = shared_ptr<SweepData>(new SweepData(stype, profile, loc, added, maxd,
+						   avd, snum_in, rad, ang));
+    }
+	
+  // Bounding box and principal curvature summary
+  maxk2_ = std::numeric_limits<double>::lowest();
+  mink2_ = std::numeric_limits<double>::max();
+  maxk1_ = std::numeric_limits<double>::lowest();
+  mink1_ = std::numeric_limits<double>::max();
+  MAH_ = MAK_ = avH_ = avK_ = 0.0;
+  bbox_ = BoundingBox(3);
+  if (group_points_.size() > 0)
+    {
+      double fac = 1.0/(double)group_points_.size();
+      for  (size_t kj=0; kj<group_points_.size(); ++kj)
+	{
+	  double k1 = group_points_[kj]->minPrincipalCurvature();
+	  double k2 = group_points_[kj]->maxPrincipalCurvature();
+	  double H = group_points_[kj]->meanCurvature();
+	  double K = group_points_[kj]->GaussCurvature();
+	  mink1_ = std::min(mink1_, fabs(k1));
+	  maxk1_ = std::max(maxk1_, fabs(k1));
+	  mink2_ = std::min(mink2_, fabs(k2));
+	  maxk2_ = std::max(maxk2_, fabs(k2));
+	  avH_ += fac*H;
+	  avK_ += fac*K;
+	  MAH_ += fac*fabs(H);
+	  MAK_ += fac*fabs(K);
+	  Vector3D point = group_points_[kj]->getPoint();
+	  Point point2(point[0], point[1], point[2]);
+	  bbox_.addUnionWith(point2);
+	}
+  
+      normalcone_ = DirectionCone(group_points_[0]->getLocFuncNormal());
+      normalcone2_ = DirectionCone(group_points_[0]->getTriangNormal());
+      avnorm_ = Point(0.0, 0.0, 0.0);
+      avnorm2_ = Point(0.0, 0.0, 0.0);
+      for  (size_t kj=1; kj<group_points_.size(); ++kj)
+	{
+	  Point norm = group_points_[kj]->getLocFuncNormal();
+	  normalcone_.addUnionWith(norm);
+	  avnorm_ += fac*norm;
+	  Point norm2 = group_points_[kj]->getTriangNormal();
+	  normalcone2_.addUnionWith(norm2);
+	  avnorm2_ += fac*norm2;
+	}
+    }
+}
+
+//===========================================================================
+void RevEngRegion::getRemainingPoints(vector<RevEngPoint*>& curr_pts,
+				      vector<RevEngPoint*>& remaining)
+//===========================================================================
+{
+  remaining.clear();
+  for (size_t ki=0; ki<group_points_.size(); ++ki)
+    {
+      auto it = std::find(curr_pts.begin(), curr_pts.end(), group_points_[ki]);
+      if (it == curr_pts.end())
+	remaining.push_back(group_points_[ki]);
+    }
+}
+
+//===========================================================================
+
+void RevEngRegion::writeSubTriangulation(std::ostream& of)
+{
+  of << group_points_.size() << std::endl;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      vector<ftSamplePoint*> next = group_points_[kr]->getNeighbours();
+      size_t nmb_next = next.size();
+      for (int ka=(int)(next.size()-1); ka>=0; --ka)
+	{
+	  RevEngPoint *pr = dynamic_cast<RevEngPoint*>(next[ka]);
+	  if (pr->region() != this)
+	    next.erase(next.begin()+ka);
+	}
+
+      int bd = (next.size() < nmb_next) ? 1 : 0;
+      of << kr << " " << group_points_[kr]->getPoint() << " " << bd << std::endl;
+      of << next.size() << " ";
+      for (size_t kh=0; kh<next.size(); ++kh)
+	{
+	  vector<RevEngPoint*>::iterator it = std::find(group_points_.begin(),
+							group_points_.end(), next[kh]);
+	  if (it == group_points_.end())
+	    {
+#ifdef DEBUG
+	      std::cout << "writeSubTriangulation: missing connection" << std::endl;
+#endif
+	    }
+	  else
+	    {
+	      size_t ix = it - group_points_.begin();
+	      of << ix << " ";
+	    }
+	}
+      of << std::endl;
+    }
+}
+
+void RevEngRegion::writeSurface(std::ostream& of)
+{
+  if (associated_sf_.size() == 0)
+    return;
+  shared_ptr<ParamSurface> surf = associated_sf_[0]->surface();
+  shared_ptr<ElementarySurface> elemsf =
+    dynamic_pointer_cast<ElementarySurface,ParamSurface>(surf);
+  if (elemsf.get())
+    {
+      // double umin = std::numeric_limits<double>::max();
+      // double umax = std::numeric_limits<double>::lowest();
+      // double vmin = std::numeric_limits<double>::max();
+      // double vmax = std::numeric_limits<double>::lowest();
+      // for (size_t ki=0; ki<group_points_.size(); ++ki)
+      // 	{
+      // 	  Vector2D par = group_points_[ki]->getPar();
+      // 	  umin = std::min(umin, par[0]);
+      // 	  umax = std::max(umax, par[0]);
+      // 	  vmin = std::min(vmin, par[1]);
+      // 	  vmax = std::max(vmax, par[1]);
+      // 	}
+      double umin = domain_[0];
+      double umax = domain_[1];
+      double vmin = domain_[2];
+      double vmax = domain_[3];
+      shared_ptr<ElementarySurface> elemsf2(elemsf->clone());
+      if (elemsf2->instanceType() != Class_Plane && umax-umin > 2*M_PI)
+	{
+	  umin = 0;
+	  umax = 2*M_PI;
+	}
+      if (elemsf2->instanceType() != Class_Plane && elemsf2->instanceType() != Class_Sphere &&
+	  (umin < -2.0*M_PI || umax > 2.0*M_PI))
+	{
+	  umin = 0;
+	  umax = 2*M_PI;
+	}
+      if (elemsf2->instanceType() == Class_Sphere && (umin < 0.0 || umax > 2.0*M_PI))
+	{
+	  umin = 0;
+	  umax = 2*M_PI;
+	}
+      
+     if (elemsf2->instanceType() == Class_Sphere &&
+	 (vmax-vmin > M_PI || vmin < -0.5*M_PI || vmax > 0.5*M_PI))
+       {
+	 vmin = -0.5*M_PI;
+	 vmax = 0.5*M_PI;
+       }
+      if (elemsf2->instanceType() == Class_Torus &&
+	  (vmax - vmin > 2*M_PI || vmin < -2.0*M_PI || vmax > 2.0*M_PI))
+       {
+	 vmin = 0;
+	 vmax = 2*M_PI;
+       }
+
+      if (elemsf2->isBounded())
+	{
+	  RectDomain dom = elemsf2->getParameterBounds();
+	  double udel = umax - umin;
+	  double vdel = vmax - vmin;
+	  if (dom.umax() - dom.umin() < 2.0*udel)
+	    {
+	      umin = dom.umin();
+	      umax = dom.umax();
+	    }
+	  if (dom.vmax() - dom.vmin() < 2.0*vdel)
+	    {
+	      vmin = dom.vmin();
+	      vmax = dom.vmax();
+	    }
+	}
+      if (umax > umin && vmax > vmin)
+	elemsf2->setParameterBounds(umin, vmin, umax, vmax);
+      elemsf2->writeStandardHeader(of);
+      elemsf2->write(of);
+    }
+  else
+    {
+      surf->writeStandardHeader(of);
+      surf->write(of);
+    }
+}
+
+void RevEngRegion::writeRegionInfo(std::ostream& of)
+{
+  double len = bbox_.low().dist(bbox_.high());
+  double ll = 0.05*len;
+  of << "400 1 0 4 100 0 155 255" << std::endl;
+  of << group_points_.size() << std::endl;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    of << group_points_[kr]->getPoint() << std::endl;
+  of << "410 1 0 4 200 55 0 255" << std::endl;
+  of << group_points_.size() << std::endl;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      Vector3D xyz = group_points_[kr]->getPoint();
+      Point xyz2(xyz[0], xyz[1], xyz[2]);
+      Point norm = group_points_[kr]->getLocFuncNormal();
+      of << xyz2 << " " << xyz2 + ll*norm << std::endl;
+    }
+  
+  of << "410 1 0 4 0 100  155 255" << std::endl;
+  of << group_points_.size() << std::endl;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      Vector3D xyz = group_points_[kr]->getPoint();
+      Point xyz2(xyz[0], xyz[1], xyz[2]);
+      Point vec = group_points_[kr]->getTriangNormal(); //minCurvatureVec();
+      of << xyz2 << " " << xyz2 + ll*vec << std::endl;
+    }
+
+  // double lambda[2];
+  // Point eigen1, eigen2, eigen3;
+  // getPCA(lambda, eigen1, eigen2, eigen3);
+  // std::ofstream of2("points_two_tangents.txt");
+  // of2 << group_points_.size() << std::endl;
+  // for (size_t kr=0; kr<group_points_.size(); ++kr)
+  //   {
+  //     Vector3D xyz = group_points_[kr]->getPoint();
+  //     Point norm = group_points_[kr]->getLocFuncNormal();
+  //     Point vec1 = eigen1 - (eigen1*norm)*norm;
+  //     vec1.normalize();
+  //     Point vec2 = norm.cross(vec1);
+  //     vec2.normalize();
+  //     of2 << xyz << " " << vec1 << " " << vec2 << std::endl;
+  //   }
+}
+
+void RevEngRegion::writeRegionPoints(std::ostream& of)
+{
+  of << "400 1 0 4 100 0 155 255" << std::endl;
+  of << group_points_.size() << std::endl;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    of << group_points_[kr]->getPoint() << std::endl;
+}  
+
+void RevEngRegion::writeAdjacentPoints(std::ostream& of)
+{
+  for (auto it=adjacent_regions_.begin(); it!=adjacent_regions_.end(); ++it)
+    {
+      (*it)->writeRegionPoints(of);
+      if ((*it)->hasSurface())
+	{
+	  shared_ptr<ParamSurface> tmp((*it)->getSurface(0)->surface()->clone());
+	  if (!tmp->isBounded())
+	    {
+	      shared_ptr<ElementarySurface> elem =
+		dynamic_pointer_cast<ElementarySurface,ParamSurface>(tmp);
+	      double dom[4];
+	      (*it)->getDomain(dom);
+	      try {
+	      if (elem.get())
+		elem->setParameterBounds(dom[0], dom[2], dom[1], dom[3]);
+	      }
+	      catch (...)
+		{
+		}
+	    }
+	  tmp->writeStandardHeader(of);
+	  tmp->write(of);
+	}
+    }
+}
+
+void RevEngRegion::writeUnitSphereInfo(std::ostream& of)
+{
+  of << "400 1 0 4 100  0 155 255" << std::endl;
+  of << group_points_.size() << std::endl;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(group_points_[kr]);
+      Point norm = pt->getLocFuncNormal();
+      of << norm << std::endl;
+    }
+  Sphere sph(1.0, Point(0.0, 0.0, 0.0), Point(0.0, 0.0, 1.0),
+	     Point(1.0, 0.0, 0.0));
+  sph.writeStandardHeader(of);
+  sph.write(of);
+
+  of << "400 1 0 4 0 100  155 255" << std::endl;
+  of << group_points_.size() << std::endl;
+  for (size_t kr=0; kr<group_points_.size(); ++kr)
+    {
+      RevEngPoint *pt = dynamic_cast<RevEngPoint*>(group_points_[kr]);
+      Point vec = pt->minCurvatureVec();
+      of << vec << std::endl;
+    }
+  of << std::endl;
+}
diff --git a/compositemodel/src/RevEngUtils.C b/compositemodel/src/RevEngUtils.C
new file mode 100644
index 00000000..de10ec7d
--- /dev/null
+++ b/compositemodel/src/RevEngUtils.C
@@ -0,0 +1,3203 @@
+/*
+ * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
+ * Applied Mathematics, Norway.
+ *
+ * Contact information: E-mail: tor.dokken@sintef.no                      
+ * SINTEF ICT, Department of Applied Mathematics,                         
+ * P.O. Box 124 Blindern,                                                 
+ * 0314 Oslo, Norway.                                                     
+ *
+ * This file is part of GoTools.
+ *
+ * GoTools is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version. 
+ *
+ * GoTools is distributed in the hope that it will be useful,        
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of         
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with GoTools. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In accordance with Section 7(b) of the GNU Affero General Public
+ * License, a covered work must retain the producer line in every data
+ * file that is created or manipulated using GoTools.
+ *
+ * Other Usage
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial activities involving the GoTools library without
+ * disclosing the source code of your own applications.
+ *
+ * This file may be used in accordance with the terms contained in a
+ * written agreement between you and SINTEF ICT. 
+ */
+
+#include "GoTools/compositemodel/RevEngUtils.h"
+#include "GoTools/compositemodel/ImplicitApprox.h"
+#include "GoTools/utils/MatrixXD.h"
+#include "GoTools/utils/LUDecomp.h"
+#include "GoTools/creators/SmoothSurf.h"
+#include "GoTools/creators/ApproxSurf.h"
+#include "GoTools/creators/SmoothCurve.h"
+#include "GoTools/creators/ApproxCurve.h"
+#include "GoTools/geometry/SplineSurface.h"
+#include "GoTools/geometry/SplineCurve.h"
+#include "GoTools/geometry/Cylinder.h"
+#include "GoTools/geometry/Cone.h"
+#include "GoTools/geometry/Sphere.h"
+#include "GoTools/geometry/Torus.h"
+#include "GoTools/geometry/Plane.h"
+#include "GoTools/geometry/Line.h"
+#include "GoTools/geometry/BoundedSurface.h"
+#include "GoTools/geometry/SISLconversion.h"
+#include "GoTools/geometry/GeometryTools.h"
+#include "GoTools/geometry/GoIntersections.h"
+#include "GoTools/geometry/SplineDebugUtils.h"
+#include "sislP.h"
+#include "newmat.h"
+#include "newmatap.h"
+#include <fstream>
+
+using namespace Go;
+using std::vector;
+using std::pair;
+
+//#define DEBUG
+//#define DEBUG_BLEND
+//#define DEBUG_CONE
+//#define DEBUG_APPROX
+
+typedef MatrixXD<double, 3> Matrix3D;
+
+//===========================================================================
+void RevEngUtils::principalAnalysis(std::vector<RevEngPoint*>& points, 
+				    double lambda[3], double eigenvec[3][3])
+//===========================================================================
+{
+  if (points.size() < 5)
+    return;
+  vector<Point> remaining(points.size()-1);
+  Vector3D xyz = points[0]->getPoint();
+  Point curr(xyz[0], xyz[1], xyz[2]);
+  for (size_t ki=1; ki<points.size(); ++ki)
+    {
+      xyz = points[ki]->getPoint();
+      Point curr2(xyz[0], xyz[1], xyz[2]);
+      remaining[ki-1] = curr2;
+    }
+  principalAnalysis(curr, remaining, lambda, eigenvec);
+}
+
+//===========================================================================
+void RevEngUtils::principalAnalysis(Point& curr, vector<Point>& points, 
+				    double lambda[3], double eigenvec[3][3])
+//===========================================================================
+{
+  // Compute covariance matrix
+  int numpt = (int)points.size();
+  double comat[3][3];
+  Point mean = curr;
+  for (int kr=0; kr<numpt; ++kr)
+    mean += points[kr];
+  mean /= (double)(numpt+1);
+
+  vector<double> wgt(numpt+1);
+  double div = (double)((numpt+1)*(numpt+1));
+  wgt[0] = (mean.dist(curr))/div;
+  for (int kr=0; kr<numpt; ++kr)
+    {
+      double dr2 = mean.dist2(points[kr]);
+      wgt[kr+1] = exp(-dr2/div);
+    }
+  
+  for (int ki=0; ki<3; ++ki)
+    for (int kj=0; kj<3; ++kj)
+      {
+	double tmp = 0.0;
+	tmp += wgt[0]*(curr[ki] - mean[ki])*(curr[kj] - mean[kj]);
+	for (int kr=0; kr<numpt; ++kr)
+	  {
+	    tmp += wgt[kr+1]*(points[kr][ki] - mean[ki])*(points[kr][kj] - mean[kj]);
+	  }
+	comat[ki][kj] = tmp/(double)(numpt);
+      }
+  
+  // Compute singular values
+  NEWMAT::Matrix nmat;
+  nmat.ReSize(3, 3);
+  for (int ki = 0; ki < 3; ++ki) {
+    for (int kj = 0; kj < 3; ++kj) {
+      nmat.element(ki, kj) = comat[ki][kj];
+    }
+  }
+      
+  static NEWMAT::DiagonalMatrix diag;
+  static NEWMAT::Matrix V;
+  try {
+    NEWMAT::SVD(nmat, diag, nmat, V);
+  } catch(...) {
+    //std::cout << "Exception in SVD" << std::endl;
+    return;
+  }
+  
+  // Singular values
+  for (int ki=0; ki<3; ++ki)
+    {
+      lambda[ki] = diag.element(ki, ki);
+      for (int kj=0; kj<3; ++kj)
+	eigenvec[ki][kj] = V.element(kj, ki);
+    }
+}
+
+
+//===========================================================================
+void RevEngUtils::computeLocFunc(Point& curr, std::vector<Point>& points,
+			       Point& vec1, Point& vec2, Point& normal, Point& mincvec,
+			       double& minc, Point& maxcvec, double& maxc,
+			       double& currdist, double& avdist)
+//===========================================================================
+{
+  // Transform points to coordinate system given by vec1 (x-axis) and vec2 (y-axis)
+  Matrix3D mat1, mat2, rotmat;
+  Vector3D vec1_2(vec1[0], vec1[1], vec1[2]);
+  Vector3D vec2_2(vec2[0], vec2[1], vec2[2]);
+  Vector3D xaxis(1, 0, 0);
+  Vector3D zaxis(0, 0, 1);
+  mat1.setToRotation(vec1_2, xaxis);
+  Vector3D v1 = mat1*vec1_2;
+  Vector3D vec2_3 = mat1*vec2_2;
+  mat2.setToRotation(vec2_3, zaxis);
+  Vector3D v2 = mat2*vec2_3;
+  rotmat = mat2*mat1;
+  //rotmat.identity();
+
+  // Perform rotation and sort parameter values and z-value
+  int nmbpts = (int)points.size() + 1;
+  vector<double> par(2*nmbpts);
+  vector<double> zval(nmbpts);
+  par[0] = curr[0];
+  par[1] = curr[1];
+  zval[0] = curr[2];
+  for (int ki=1; ki<nmbpts; ++ki)
+    {
+      Point dv = points[ki-1] - curr;
+      Vector3D dv2(dv[0], dv[1], dv[2]);
+      Vector3D dvrot = rotmat*dv2;
+      //Vector3D dvrot = mat2*dvrot0;
+      par[2*ki] = curr[0] + dvrot[0];
+      par[2*ki+1] = curr[1] + dvrot[1];
+      zval[ki] = curr[2] + dvrot[2];
+    }
+
+  // Approximate z-component by biquadratic Bezier function in x and y
+  int order = 3;
+  shared_ptr<SplineSurface> locsf = RevEngUtils::surfApprox(zval, 1, par, order,
+							      order, order, order);
+
+  vector<double> coefs2(3*order*order);
+  std::vector<double>::iterator cf = locsf->coefs_begin();
+  for (int ka=0; ka<order; ++ka)
+    {
+      double vpar = locsf->basis_v().grevilleParameter(ka);
+      for (int kb=0; kb<order; ++kb, ++cf)
+	{
+	  double upar = locsf->basis_u().grevilleParameter(kb);
+	  coefs2[(ka*order+kb)*3] = upar;
+	  coefs2[(ka*order+kb)*3+1] = vpar;
+	  coefs2[(ka*order+kb)*3+2] = *cf;
+	}
+    }
+  shared_ptr<SplineSurface> tmp(new SplineSurface(order, order, order, order, 
+						  locsf->basis_u().begin(),
+						  locsf->basis_v().begin(), &coefs2[0], 3));
+#ifdef DEBUG
+  int writesurface = 0;
+  if (writesurface)
+    {
+      std::ofstream of("approx_sf.g2");
+      tmp->writeStandardHeader(of);
+      tmp->write(of);
+      of << "400 1 0 4 0 255 0 255" << std::endl;
+      of << 1 << std::endl;
+      of << curr << std::endl;
+      of << "400 1 0 4 255 0 0 255" << std::endl;
+      of << nmbpts << std::endl;
+      for (int ka=0; ka<nmbpts; ++ka)
+	{
+	  Point tmppt(par[2*ka], par[2*ka+1], zval[ka]);
+	  of << tmppt << std::endl;
+	}
+    }
+#endif
+						  
+  // Compute surface normal in curr
+  vector<Point> der(3);
+  locsf->point(der, par[0], par[1], 1);
+  Vector3D norm(-der[1][0], -der[2][0], 1.0);
+  norm.normalize();
+
+  // Accuracy of approximation
+  currdist = fabs(zval[0] - der[0][0]);
+  avdist = currdist;
+  for (int ki=1; ki<nmbpts; ++ki)
+    {
+      Point pos;
+      locsf->point(pos, par[2*ki], par[2*ki+1]);
+      avdist += fabs(zval[ki] - pos[0]);
+    }
+  avdist /= (double)nmbpts;
+  
+  // Compute principal curvatures in curr
+  shared_ptr<SISLSurf> sislsf(GoSurf2SISL(*locsf, false));
+  int left1 = 0, left2 = 0;
+  int stat = 0;
+  double k1, k2;
+  double d1[2], d2[2];
+  s2542(sislsf.get(), 0, 0, 0, &par[0], &left1, &left2, &k1, &k2, d1, d2, &stat);
+  Vector3D du(1.0, 0.0, der[1][0]);
+  Vector3D dv(0.0, 1.0, der[2][0]);
+  Vector3D cvec1 = d1[0]*du + d1[1]*dv;
+  Vector3D cvec2 = d2[0]*du + d2[1]*dv;
+  minc = k1;
+  maxc = k2;
+
+  // Vector3D origin(par[0], par[1], zval[0]);
+  // of << "410 1 0 4 0 0 0 255" << std::endl;
+  // of << "1" << std::endl;
+  // of << origin << " " << origin+norm << std::endl;
+
+  // of << "410 1 0 4 0 55 155 255" << std::endl;
+  // of << "1" << std::endl;
+  // of << origin << " " << origin+cvec1 << std::endl;
+
+  
+  // of << "410 1 0 4 155 55 0 255" << std::endl;
+  // of << "1" << std::endl;
+  // of << origin << " " << origin+cvec2 << std::endl;
+
+  
+  
+  // Transform results to original coordinate system
+  Matrix3D mat3, mat4, rotmat2;
+  mat4.setToRotation(zaxis, vec2_3);
+  mat3.setToRotation(xaxis, vec1_2);
+  rotmat2 = mat3*mat4;
+  //rotmat2.identity();
+  //Vector3D norm0 = mat4*norm;
+  Vector3D norm2 = rotmat2*norm;
+  normal = Point(norm2[0], norm2[1], norm2[2]);
+  
+  Vector3D cvec3 = rotmat2*cvec1;
+  mincvec = Point(cvec3[0], cvec3[1],cvec3[2]); 
+  Vector3D cvec4 = rotmat2*cvec2;
+  maxcvec = Point(cvec4[0], cvec4[1],cvec4[2]); 
+
+  int stop_break = 1;
+}
+
+//===========================================================================
+void RevEngUtils::smoothSurf(shared_ptr<SplineSurface>& surf, int fixed)
+//===========================================================================
+{
+  SmoothSurf smooth;
+  int ncoef1 = surf->numCoefs_u();
+  int ncoef2 = surf->numCoefs_v();
+  vector<int> coef_known(ncoef1*ncoef2, 0);
+  for (int ka=0; ka<ncoef1; ++ka)
+    {
+      for (int kb=0; kb<fixed; kb++)
+	{
+	  coef_known[kb*ncoef1+ka] = 1;
+	  coef_known[(ncoef2-kb-1)*ncoef1+ka] = 1;
+	}
+    }
+  for (int kb=fixed; kb<ncoef2-fixed; ++kb)
+    {
+      for (int ka=0; ka<fixed; ++ka)
+	{
+	  coef_known[kb*ncoef1+ka] = 1;
+	  coef_known[(kb+1)*ncoef1-ka-1] = 1;
+	}
+    }
+
+
+  int close1 = GeometryTools::analyzePeriodicityDerivs(*surf, 0, 1);
+  int close2 = GeometryTools::analyzePeriodicityDerivs(*surf, 1, 1);
+  int seem[2];
+  seem[0] = (close1 >= 0) ? 1 : 0;
+  seem[1] = (close2 >= 0) ? 1 : 0;
+  shared_ptr<SplineSurface> surf2(surf->clone());
+  smooth.attach(surf2, seem, &coef_known[0]);
+
+  double wgt1 = 0.0, wgt2 = 1.0, wgt3 = 0.0;
+  smooth.setOptimize(wgt1, wgt2, wgt3);
+  shared_ptr<SplineSurface> surf3;
+  smooth.equationSolve(surf3);
+  std::swap(surf, surf3);
+}
+
+//===========================================================================
+shared_ptr<SplineSurface>
+RevEngUtils::surfApprox(vector<double>& data, int dim, vector<double>& param,
+			int order1, int order2, int ncoef1, int ncoef2,
+			bool close1, bool close2,
+			int max_iter, double tol, double& maxd, double& avd, 
+			int& num_out, vector<double>& parvals, double belt_frac)
+//===========================================================================
+{
+  // Create initial spline space
+  double umin = std::numeric_limits<double>::max();
+  double umax = std::numeric_limits<double>::lowest();
+  double vmin = std::numeric_limits<double>::max();
+  double vmax = std::numeric_limits<double>::lowest();
+  for (size_t ki=0; ki<param.size(); ki+=2)
+    {
+      umin = std::min(umin, param[ki]);
+      umax = std::max(umax, param[ki]);
+      vmin = std::min(vmin, param[ki+1]);
+      vmax = std::max(vmax, param[ki+1]);
+    }
+  double udel = (umax - umin);
+  double vdel = (vmax - vmin);
+  if (!close1)
+    {
+      umin -= belt_frac*udel;
+      umax += belt_frac*udel;
+    }
+  if (!close2)
+    {
+      vmin -= belt_frac*vdel;
+      vmax += belt_frac*vdel;
+    }
+  udel = (umax - umin)/(double)(ncoef1 - order1 + 1);
+  vdel = (vmax - vmin)/(double)(ncoef2 - order2 + 1);
+  vector<double> et1(order1+ncoef1);
+  vector<double> et2(order2+ncoef2);
+  vector<double> coef(ncoef1*ncoef2*dim, 0.0);
+  for (int ka=0; ka<order1; ++ka)
+    {
+      et1[ka] = umin;
+      et1[ka+ncoef1] = umax;
+    }
+  for (int ka=0; ka<order2; ++ka)
+    {
+      et2[ka] = vmin;
+      et2[ka+ncoef2] = vmax;
+    }
+  for (int ka=0; ka<ncoef1-order1; ++ka)
+    et1[order1+ka] = umin + (ka+1)*udel; 
+  for (int ka=0; ka<ncoef2-order2; ++ka)
+    et2[order2+ka] = vmin + (ka+1)*vdel; 
+
+  
+ shared_ptr<SplineSurface> surf(new SplineSurface(ncoef1, ncoef2, order1,
+						  order2, &et1[0], 
+						  &et2[0], &coef[0], dim));
+ shared_ptr<SplineSurface> surf1(new SplineSurface(ncoef1, ncoef2, order1,
+						   order2, &et1[0], 
+						   &et2[0], &coef[0], dim));
+
+ // Approximate
+  SmoothSurf approx2;
+  vector<int> coef_known(ncoef1*ncoef2, 0);
+  int seem[2];
+  seem[0] = close1 ? 1 : 0;
+  seem[1] = close2 ? 1 : 0;
+  int nmbpts = (int)data.size()/dim;
+  vector<double> ptwgt(nmbpts, 1.0);
+  approx2.attach(surf1, seem, &coef_known[0]);
+
+  //double wgt1 = 0.0, wgt2 = 0.001, wgt3 = 0.001;
+  double wgt1 = 0.0, wgt2 = 0.001, wgt3 = 0.001;
+  double approxwgt = 1.0 - wgt1 - wgt2 - wgt3;
+  approx2.setOptimize(wgt1, wgt2, wgt3);
+  approx2.setLeastSquares(data, param, ptwgt, approxwgt);
+  shared_ptr<SplineSurface> surf3;
+  approx2.equationSolve(surf3);
+#ifdef DEBUG
+  std::ofstream of("first_approx.g2");
+  surf3->writeStandardHeader(of);
+  surf3->write(of);
+#endif
+  
+  ApproxSurf approx(surf3, data, param, dim, tol);
+  //ApproxSurf approx(surf3, data, param, dim, tol, 0, false, true, 0, true);
+  approx.setMBA(true);
+  approx.setFixBoundary(false);
+  double acc_frac = 0.6;
+  approx.setAccuracyCrit(1, acc_frac);
+ shared_ptr<SplineSurface> surf2 = approx.getApproxSurf(maxd, avd, num_out, max_iter);
+ if (surf2.get())
+   parvals = approx.getParvals();
+ return surf2;
+}
+
+//===========================================================================
+shared_ptr<SplineSurface> RevEngUtils::surfApprox(vector<double>& data, int dim,
+						  vector<double>& param, int order1,
+						  int order2, int nmb_coef1, int nmb_coef2,
+						  double del)
+//===========================================================================
+{
+  // Define spline space
+  double umin, umax, vmin, vmax;
+  umin = umax = param[0];
+  vmin = vmax = param[1];
+  for (size_t kj=2; kj<param.size(); kj+=2)
+    {
+      umin = std::min(umin, param[kj]);
+      umax = std::max(umax, param[kj]);
+      vmin = std::min(vmin, param[kj+1]);
+      vmax = std::max(vmax, param[kj+1]);
+    }
+  umin -= del;
+  umax += del;
+  vmin -= del;
+  vmax += del;
+
+  return surfApprox(data, dim, param, order1, order2, nmb_coef1, nmb_coef2,
+		    umin, umax, vmin, vmax);
+}
+  
+//===========================================================================
+shared_ptr<SplineSurface> RevEngUtils::surfApprox(vector<double>& data, int dim,
+						  vector<double>& param, int order1,
+						  int order2, int nmb_coef1, int nmb_coef2,
+						  double umin, double umax,
+						  double vmin, double vmax)
+//===========================================================================
+{
+  double udel = (umax - umin)/(double)(nmb_coef1-order1+1);
+  double vdel = (vmax - vmin)/(double)(nmb_coef2-order2+1);
+  
+  vector<double> knots1(order1+nmb_coef1), knots2(order2+nmb_coef2);
+  for (int ka=0; ka<order1; ++ka)
+    {
+      knots1[ka] = umin;
+      knots1[nmb_coef1+ka] = umax;
+    }
+  for (int ka=order1; ka<nmb_coef1; ++ka)
+    knots1[ka] = umin + (ka-order1+1)*udel;
+  
+  for (int ka=0; ka<order2; ++ka)
+    {
+      knots2[ka] = vmin;
+      knots2[nmb_coef2+ka] = vmax;
+    }
+  for (int ka=order2; ka<nmb_coef2; ++ka)
+    knots2[ka] = vmin + (ka-order2+1)*vdel;
+  
+
+  vector<double> coefs((nmb_coef1+order1)*(nmb_coef2+order2)*dim, 0.0);
+  shared_ptr<SplineSurface> bez(new SplineSurface(nmb_coef1, nmb_coef2, order1, order2, 
+						  &knots1[0], &knots2[0], &coefs[0], dim));
+
+  // Approximate
+  SmoothSurf approx;
+  vector<int> coef_known((nmb_coef1+order1)*(nmb_coef2+order2), 0);
+  int seem[2];
+  seem[0] = seem[1] = 0;
+  int nmbpts = (int)data.size()/dim;
+  vector<double> ptwgt(nmbpts, 1.0);
+  approx.attach(bez, seem, &coef_known[0]);
+
+  //double wgt1 = 0.0, wgt2 = 0.001, wgt3 = 0.001;
+  double wgt1 = 0.0, wgt2 = 0.05, wgt3 = 0.05;
+  double approxwgt = 1.0 - wgt1 - wgt2 - wgt3;
+  approx.setOptimize(wgt1, wgt2, wgt3);
+  approx.setLeastSquares(data, param, ptwgt, approxwgt);
+
+  shared_ptr<SplineSurface> surf;
+  approx.equationSolve(surf);
+  return surf;
+ }
+
+//===========================================================================
+bool RevEngUtils::parameterizeOnPrimary(vector<RevEngPoint*>& points,
+					shared_ptr<ParamSurface> surf,
+					vector<double>& data, vector<double>& param,
+					int& inner1, int& inner2, bool& close1,
+					bool& close2)
+//===========================================================================
+{
+  double eps = 1.0e-2;
+  double angtol = 0.1;
+
+  // Make sure to use an untrimmed surface
+  shared_ptr<ParamSurface> sf = surf;
+  shared_ptr<BoundedSurface> bdsf = dynamic_pointer_cast<BoundedSurface,ParamSurface>(sf);
+  if (bdsf.get())
+    sf = bdsf->underlyingSurface();
+  shared_ptr<Cylinder> cyl = dynamic_pointer_cast<Cylinder,ParamSurface>(sf);
+  shared_ptr<Cone> cone = dynamic_pointer_cast<Cone,ParamSurface>(sf);
+  shared_ptr<Sphere> sph = dynamic_pointer_cast<Sphere,ParamSurface>(sf);
+  shared_ptr<Torus> torus = dynamic_pointer_cast<Torus,ParamSurface>(sf);
+  close1 = (cyl.get() || cone.get() || sph.get() || torus.get());
+  close2 = (sph.get() || torus.get());
+  RectDomain dom = sf->containingDomain();
+  double u1 = dom.umin();
+  double u2 = dom.umax();
+  double udel = u2 - u1;
+  double v1 = dom.vmin();
+  double v2 = dom.vmax();
+  double vdel = v2 - v1;
+
+  // Parameterize
+  int dim = surf->dimension();
+  param.resize(2*points.size());
+  data.reserve(dim*points.size());
+  double umin = std::numeric_limits<double>::max();
+  double umax = std::numeric_limits<double>::lowest();
+  double vmin = std::numeric_limits<double>::max();
+  double vmax = std::numeric_limits<double>::lowest();
+  double sfac = 0.5;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pos(xyz[0], xyz[1], xyz[2]);
+      double upar, vpar, dist;
+      Point close;
+      sf->closestPoint(pos, upar, vpar, close, dist, eps);
+
+      if (sf->isBounded() && dist > 0.1) //eps) TEST
+	{
+	  // Check boundary
+	  double uparbd, vparbd, distbd;
+	  Point closebd;
+	  double seed[2];
+	  seed[0] = upar;
+	  seed[1] = vpar;
+	  RectDomain dom = sf->containingDomain();
+	  sf->closestBoundaryPoint(pos, uparbd, vparbd, closebd, distbd, eps, &dom, seed);
+	  if (fabs(distbd - dist) < 0.1*dist)
+	    {
+	      // Angular check to see if a true closest point is found
+	      Point norm;
+	      sf->normal(norm, upar, vpar);
+	      Point vec = pos - close;
+	      double ang = norm.angle(vec);
+	      ang = std::min(ang, fabs(M_PI - ang));
+	      if (ang > angtol)
+		return false;
+	    }
+	}
+
+      // Check with seam
+      // if (ki > 0 && close1 && ((upar-u1 < umin-upar && u2-umax < umin-u1) ||
+      // 			       (u2-upar < upar-umax && umin-u1 < u2-umax)))
+      // if (ki > 0 && close1 && (upar < umin || upar > umax) &&
+      // 	  (fabs(umin-upar+udel) < fabs(upar-umin) || fabs(upar+udel-umax) < fabs(umax-upar)))
+	  if (ki > 0 && close1 && (upar < umin || upar > umax) &&
+	      std::min(fabs(umin-upar+udel),fabs(upar+udel-umax)) < std::min(fabs(upar-umin),fabs(upar-umax)))
+	{
+	  // if (ki >= 2)
+	  //   {
+	  //     // Compare distances
+	  //     double d1 = points[ki-2]->pntDist(points[ki-1]);
+	  //     double d2 = points[ki-1]->pntDist(points[ki]);
+	  //     Point p1(param[2*(ki-2)], param[2*(ki-2)+1]);
+	  //     Point p2(param[2*(ki-1)], param[2*(ki-1)+1]);
+	  //     Point p3(upar,vpar);
+	  //     double dp1 = p1.dist(p2);
+	  //     double dp2 = p2.dist(p3);
+	  //     if (dp1/dp2 < sfac*d1/d2)
+	  // 	{
+		  if (upar-u1 < u2-upar)
+		    upar += (u2-u1);
+		  else
+		    upar -= (u2-u1);
+	    // 	}
+									 
+	    //   int stop_break1 = 1;
+	    // }
+	}
+      // if (ki > 0 && close2 && ((vpar-v1 < vmin-vpar && v2-vmax < vmin-v1) ||
+      // 			       (v2-vpar < vpar-vmax && vmin-v1 < v2-vmax)))
+	  // if (ki > 0 && close2 && (vpar < vmin || vpar > vmax) &&
+	  //     (fabs(vmin-vpar+vdel) < fabs(vpar-vmin) || fabs(vpar+vdel-vmax) < fabs(vpar-vmax)))
+	  if (ki > 0 && close2 && (vpar < vmin || vpar > vmax) &&
+	      std::min(fabs(vmin-vpar+vdel),fabs(vpar+vdel-vmax)) < std::min(fabs(vpar-vmin),fabs(vpar-vmax)))
+	{
+	  // if (ki >= 2)
+	  //   {
+	  //     // Compare distances
+	  //     double d1 = points[ki-2]->pntDist(points[ki-1]);
+	  //     double d2 = points[ki-1]->pntDist(points[ki]);
+	  //     Point p1(param[2*(ki-2)], param[2*(ki-2)+1]);
+	  //     Point p2(param[2*(ki-1)], param[2*(ki-1)+1]);
+	  //     Point p3(upar,vpar);
+	  //     double dp1 = p1.dist(p2);
+	  //     double dp2 = p2.dist(p3);
+	  //     if (dp1/dp2 < sfac*d1/d2)
+	  // 	{
+		  if (vpar-v1 < v2-vpar)
+		    vpar += (v2-v1);
+		  else
+		    vpar -= (v2-v1);
+	    // 	}
+									 
+	    //   int stop_break2 = 1;
+	    // }
+	}
+      param[2*ki] = upar;
+      param[2*ki+1] = vpar;
+      umin = std::min(umin, upar);
+      umax = std::max(umax, upar);
+      vmin = std::min(vmin, vpar);
+      vmax = std::max(vmax, vpar);
+      data.insert(data.end(), pos.begin(), pos.end());
+    }
+
+  if (close1 && umax-umin < 2*M_PI-eps)
+    close1 = false;
+  if (close2 && vmax-vmin < 2*M_PI-eps)
+    close2 = false;
+  
+  // Reparameterize for surfaces with circular properties
+  inner1 = inner2 = 0;
+  if (cyl.get())
+    {
+      double rad = cyl->getRadius();
+      for (size_t ki=0; ki<param.size(); ki+=2)
+	param[ki] *= rad;
+      inner1 = 2.0*(umax - umin)/M_PI;
+    }
+
+  if (cone.get())
+    {
+      double rad1 = cone->radius(0.0, vmin);
+      double rad2 = cone->radius(0.0, vmax);
+      double rad = 0.5*(rad1 + rad2);
+      for (size_t ki=0; ki<param.size(); ki+=2)
+	param[ki] *= rad;
+      inner1 = 2.0*(umax - umin)/M_PI;
+    }
+  
+  if (sph.get())
+    {
+      double rad = sph->getRadius();
+      for (size_t ki=0; ki<param.size(); ++ki)
+	param[ki] *= rad;
+      inner1 = 2.0*(umax - umin)/M_PI;
+      inner2 = 2.0*(vmax - vmin)/M_PI;
+    }
+
+  if (torus.get())
+    {
+      double rad1 = torus->getMajorRadius();
+      double rad2 = torus->getMinorRadius();
+      for (size_t ki=0; ki<param.size(); ki+=2)
+	{
+	  param[ki] *= rad1;
+	  param[ki+1] *= rad2;
+	}
+      inner1 = 2.0*(umax - umin)/M_PI;
+      inner2 = 2.0*(vmax - vmin)/M_PI;
+    }
+  return true;
+}
+
+//===========================================================================
+void RevEngUtils::parameterizeWithPlane(vector<RevEngPoint*>& pnts,
+					const BoundingBox& bbox,
+					const Point& vec1, const Point& vec2,
+					vector<double>& data, vector<double>& param)
+//===========================================================================
+{
+  vector<Point> pos(pnts.size());
+  for (size_t ki=0; ki<pnts.size(); ++ki)
+    {
+      Vector3D xyz = pnts[ki]->getPoint();
+      pos[ki] = Point(xyz[0], xyz[1], xyz[2]);
+    }
+
+  parameterizeWithPlane(pos, bbox, vec1, vec2, data, param);
+}
+
+//===========================================================================
+void RevEngUtils::parameterizeWithPlane(vector<Point>& pnts, const BoundingBox& bbox,
+					const Point& vec1, const Point& vec2,
+					vector<double>& data, vector<double>& param)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  Point mid = 0.5*(bbox.low() + bbox.high());
+  int dim = mid.dimension();
+  double diag = bbox.low().dist(bbox.high());
+  int order = 2;
+  double et[4];
+  et[0] = et[1] = -diag;
+  et[2] = et[3] = diag;
+  vector<double> coefs;
+  int sgn1, sgn2;
+  int ka, kb;
+  for (kb=0, sgn2=-1; kb<2; ++kb, sgn2=1)
+    for (ka=0, sgn1=-1; ka<2; ++ka, sgn1=1)
+      {
+	Point pos = mid+sgn1*diag*vec1+sgn2*diag*vec2;
+	coefs.insert(coefs.end(), pos.begin(), pos.end());
+      }
+
+  shared_ptr<SplineSurface> surf(new SplineSurface(order, order, order, order, &et[0], 
+						   &et[0], coefs.begin(), dim));
+#ifdef DEBUG
+  std::ofstream of("parplane.g2");
+  surf->writeStandardHeader(of);
+  surf->write(of);
+#endif
+  
+  param.resize(2*pnts.size());
+  data.reserve(dim*pnts.size());
+  for (size_t ki=0; ki<pnts.size(); ++ki)
+    {
+      double upar, vpar, dist;
+      Point close;
+      surf->closestPoint(pnts[ki], upar, vpar, close, dist, eps);
+      param[2*ki] = upar;
+      param[2*ki+1] = vpar;
+      data.insert(data.end(), pnts[ki].begin(), pnts[ki].end());
+    }
+}
+
+//===========================================================================
+void RevEngUtils::computeAxis(vector<pair<vector<RevEngPoint*>::iterator,
+			      vector<RevEngPoint*>::iterator> >& points,
+			      Point& axis, Point& Cx, Point& Cy)
+//===========================================================================
+{
+  double Cmat[3][3];
+  for (int ka=0; ka<3; ++ka)
+    for (int kb=0; kb<3; ++kb)
+      Cmat[ka][kb] = 0.0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      for (int ka=0; ka<3; ++ka)
+	for (int kb=0; kb<3; ++kb)
+	  {
+	    for (auto it=start; it!=end; ++it)
+	      {
+		RevEngPoint *pt = *it;
+		Point norm1 = pt->getLocFuncNormal();
+		Point norm2 = pt->getTriangNormal();
+		Point norm = norm1; //0.5*(norm1 + norm2);
+		Cmat[ka][kb] += norm[ka]*norm[kb];
+		//Cmat[ka][kb] += norm2[ka]*norm2[kb];
+	      }
+	  }
+    }
+  
+  // Compute singular values
+  NEWMAT::Matrix nmat;
+  nmat.ReSize(3, 3);
+  for (int ka = 0; ka < 3; ++ka) {
+    for (int kb = 0; kb < 3; ++kb) {
+      nmat.element(ka, kb) = Cmat[ka][kb];
+    }
+  }
+      
+  static NEWMAT::DiagonalMatrix diag;
+  static NEWMAT::Matrix V;
+  try {
+    NEWMAT::SVD(nmat, diag, nmat, V);
+  } catch(...) {
+    //std::cout << "Exception in SVD" << std::endl;
+    exit(-1);
+  }
+  Cx = Point(V.element(0,0), V.element(1,0), V.element(2,0));
+  Cy = Point(V.element(0,1), V.element(1,1), V.element(2,1));
+  axis = Point(V.element(0,2), V.element(1,2), V.element(2,2));
+
+}
+
+
+//===========================================================================
+void RevEngUtils::computeAxis(vector<Point>& points,
+			      Point& axis, Point& Cx, Point& Cy)
+//===========================================================================
+{
+  double Cmat[3][3];
+  for (int ka=0; ka<3; ++ka)
+    for (int kb=0; kb<3; ++kb)
+      {
+	Cmat[ka][kb] = 0.0;
+	for (size_t ki=0; ki<points.size(); ++ki)
+	  {
+	    Cmat[ka][kb] += points[ki][ka]*points[ki][kb];
+	  }
+      }
+  
+  // Compute singular values
+  NEWMAT::Matrix nmat;
+  nmat.ReSize(3, 3);
+  for (int ka = 0; ka < 3; ++ka) {
+    for (int kb = 0; kb < 3; ++kb) {
+      nmat.element(ka, kb) = Cmat[ka][kb];
+    }
+  }
+      
+  static NEWMAT::DiagonalMatrix diag;
+  static NEWMAT::Matrix V;
+  try {
+    NEWMAT::SVD(nmat, diag, nmat, V);
+  } catch(...) {
+    //std::cout << "Exception in SVD" << std::endl;
+    exit(-1);
+  }
+  Cx = Point(V.element(0,0), V.element(1,0), V.element(2,0));
+  Cy = Point(V.element(0,1), V.element(1,1), V.element(2,1));
+  axis = Point(V.element(0,2), V.element(1,2), V.element(2,2));
+
+}
+
+
+//===========================================================================
+void RevEngUtils::coneAxis(vector<pair<vector<RevEngPoint*>::iterator,
+			      vector<RevEngPoint*>::iterator> >& points,
+			      Point& axis, Point& Cx, Point& Cy)
+//===========================================================================
+{
+  size_t numpt = 0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      numpt += (points[ki].second - points[ki].first);
+    }
+  double wgt = 1.0/(double)numpt;
+
+  Point mid(3);
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      for (auto it=start; it!=end; ++it)
+	{
+	  RevEngPoint *pt = *it;
+	  Point norm = pt->getLocFuncNormal();
+	  mid += wgt*norm;
+	}
+    }
+
+  double Cmat[3][3];
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      for (int ka=0; ka<3; ++ka)
+	for (int kb=0; kb<3; ++kb)
+	  {
+	    Cmat[ka][kb] = 0.0;
+	    for (auto it=start; it!=end; ++it)
+	      {
+		RevEngPoint *pt = *it;
+		Point norm = pt->getLocFuncNormal();
+		Point vec = norm - mid;
+		Cmat[ka][kb] += vec[ka]*vec[kb];
+	      }
+	  }
+    }
+  
+  // Compute singular values
+  NEWMAT::Matrix nmat;
+  nmat.ReSize(3, 3);
+  for (int ka = 0; ka < 3; ++ka) {
+    for (int kb = 0; kb < 3; ++kb) {
+      nmat.element(ka, kb) = Cmat[ka][kb];
+    }
+  }
+      
+  static NEWMAT::DiagonalMatrix diag;
+  static NEWMAT::Matrix V;
+  try {
+    NEWMAT::SVD(nmat, diag, nmat, V);
+  } catch(...) {
+    //std::cout << "Exception in SVD" << std::endl;
+    exit(-1);
+  }
+  Cx = Point(V.element(0,0), V.element(1,0), V.element(2,0));
+  Cy = Point(V.element(0,1), V.element(1,1), V.element(2,1));
+  axis = Point(V.element(0,2), V.element(1,2), V.element(2,2));
+
+}
+
+//===========================================================================
+void RevEngUtils::coneApex(vector<pair<vector<RevEngPoint*>::iterator,
+			      vector<RevEngPoint*>::iterator> >& points,
+			   Point axis, Point& apex, double& phi)
+//===========================================================================
+{
+  double Mmat[3][3], Mi[3][3];
+  double bvec[3], bi[3];
+  for (int ka=0; ka<3; ++ka)
+    {
+      bvec[ka] = 0.0;
+      for (int kb=0; kb<3; ++kb)
+	Mmat[ka][kb] = 0.0;
+    }
+
+  int nmb = 0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    nmb += (int)(points[ki].second - points[ki].first);
+
+  vector<Point> dird;
+  vector<Point> pp;
+  dird.reserve(nmb);
+  pp.reserve(nmb);
+  double wg = 1.0/(double)nmb;
+    for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      for (auto it=start; it!=end; ++it)
+	{
+	  RevEngPoint *pt = *it;
+	  Point norm = pt->getLocFuncNormal();
+	  Point tmp = norm.cross(axis);
+	  Point di = tmp.cross(norm);
+	  di.normalize_checked();
+	  dird.push_back(di);
+	  Vector3D xyz = pt->getPoint();
+	  Point pos(xyz[0], xyz[1], xyz[2]);
+	  pp.push_back(pos);
+
+	  for (int ka=0; ka<3; ++ka)
+	    for (int kb=0; kb<3; ++kb)
+	      {
+		if (ka == kb)
+		  continue;
+		Mi[ka][kb] = -di[ka]*di[kb];
+	      }
+	  Mi[0][0] = di[1]*di[1] + di[2]*di[2];
+	  Mi[1][1] = di[0]*di[0] + di[2]*di[2];
+	  Mi[2][2] = di[0]*di[0] + di[1]*di[1];
+
+	  bi[0] = pos[0]*di[1]*di[1] - pos[1]*di[0]*di[1] - pos[2]*di[0]*di[2] + pos[0]*di[2]*di[2];
+	  bi[1] = pos[1]*di[2]*di[2] - pos[2]*di[1]*di[2] - pos[0]*di[1]*di[0] + pos[1]*di[0]*di[0];
+	  bi[2] = pos[2]*di[0]*di[0] - pos[0]*di[2]*di[0] - pos[1]*di[2]*di[1] + pos[2]*di[1]*di[1];
+	  
+	  for (int ka=0; ka<3; ++ka)
+	    {
+	      bvec[ka] += wg*bi[ka];
+	      for (int kb=0; kb<3; ++kb)
+		Mmat[ka][kb] += wg*Mi[ka][kb];
+	    }
+	}
+    }
+
+#ifdef DEBUG
+    std::ofstream of("directions.g2");
+    of << "410 1 0 4 155 200 0 255" << std::endl;
+    of << dird.size() << std::endl;
+    for (size_t ki=0; ki<dird.size(); ++ki)
+      of << pp[ki] << " " << pp[ki]+dird[ki] << std::endl;
+#endif
+    
+    double det = 0.0;
+    int sgn = 1;
+    int ka, kb, kc;
+    double ax=0.0, ay=0.0, az=0.0;
+    // for (ka=0; ka<3; ++ka, sgn*=-1)
+    //   {
+    // 	kb = (ka+1)%3;
+    // 	kc = (kb+1)%3;
+    // 	det += sgn*Mmat[0][ka]*(Mmat[1][kb]*Mmat[2][kc]-Mmat[2][kb]*Mmat[1][kc]);
+    // 	ax += sgn*bvec[ka]*(Mmat[1][kb]*Mmat[2][kc]-Mmat[2][kb]*Mmat[1][kc]);
+    // 	ay += sgn*Mmat[0][ka]*(bvec[kb]*Mmat[2][kc]-Mmat[2][kb]*bvec[kc]);
+    // 	az += sgn*Mmat[0][ka]*(Mmat[1][kb]*bvec[kc]-bvec[kb]*Mmat[1][kc]);
+    //   }
+    // apex = Point(ax/det, ay/det, az/det);
+
+    double det2 = Mmat[0][0]*(Mmat[1][1]*Mmat[2][2] - Mmat[1][2]*Mmat[2][1]) -
+      Mmat[0][1]*(Mmat[1][0]*Mmat[2][2] - Mmat[1][2]*Mmat[2][0]) +
+      Mmat[0][2]*(Mmat[1][0]*Mmat[2][1] - Mmat[1][1]*Mmat[2][0]);
+    double ax2 = bvec[0]*(Mmat[1][1]*Mmat[2][2] - Mmat[1][2]*Mmat[2][1]) -
+      bvec[1]*(Mmat[1][0]*Mmat[2][2] - Mmat[1][2]*Mmat[2][0]) +
+      bvec[2]*(Mmat[1][0]*Mmat[2][1] - Mmat[1][1]*Mmat[2][0]);
+    double ay2 = Mmat[0][0]*(bvec[1]*Mmat[2][2] - bvec[2]*Mmat[2][1]) -
+      Mmat[0][1]*(bvec[0]*Mmat[2][2] - bvec[2]*Mmat[2][0]) +
+      Mmat[0][2]*(bvec[0]*Mmat[2][1] - bvec[1]*Mmat[2][0]);
+    double az2 = Mmat[0][0]*(Mmat[1][1]*bvec[2] - Mmat[1][2]*bvec[1]) -
+      Mmat[0][1]*(Mmat[1][0]*bvec[2] - Mmat[1][2]*bvec[0]) +
+      Mmat[0][2]*(Mmat[1][0]*bvec[1] - Mmat[1][1]*bvec[0]);
+    apex = (fabs(det2) < 1.0e-6) ? Point(0.0, 0.0, 0.0) : Point(ax2/det2, ay2/det2, az2/det2);
+    
+    // // std::cout << det << " " << det2 << std::endl;
+    // for (int ka=0; ka<3; ++ka)
+    //   {
+    // 	double tmp = 0.0;
+    // 	for (kb=0; kb<3; ++kb)
+    // 	  tmp += Mmat[ka][kb]*apex[kb];
+    // 	std::cout << tmp << " " << bvec[ka] << std::endl;
+    //   }
+
+    double nom=0.0, denom=0.0;
+    for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      for (auto it=start; it!=end; ++it)
+	{
+	  RevEngPoint *pt = *it;
+	  Vector3D xyz = pt->getPoint();
+	  Point pos(xyz[0], xyz[1], xyz[2]);
+	  Point tmp1 = pos - apex;
+	  Point tmp2 = tmp1.cross(axis);
+	  nom += tmp2.length();
+	  denom += tmp1*axis;
+	}
+    }
+    
+    double tanphi = nom/denom;
+    phi = atan(tanphi);
+}
+
+//===========================================================================
+void
+RevEngUtils::computeSphereProp(vector<pair<vector<RevEngPoint*>::iterator,
+			       vector<RevEngPoint*>::iterator> >& points,
+			       Point& centre, double& radius)
+//===========================================================================
+{
+  double Amat[4][4];
+  double bvec[4];
+  // vector<vector<double> > Amat(4, vector<double>(4,0.0));
+  // vector<double> bvec(4, 0.0);
+  size_t numpt = 0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      numpt += (points[ki].second - points[ki].first);
+    }
+  
+  vector<vector<double> > A1(numpt);
+  vector<double> b1(numpt);
+  for (size_t kj=0; kj<numpt; ++kj)
+    A1[kj].resize(4);
+      
+  for (int ka=0; ka<4; ++ka)
+    {
+      for (int kb=0; kb<4; ++kb)
+  	Amat[ka][kb] = 0.0;
+      bvec[ka] = 0.0;
+    }
+
+  double wgt = 1.0/(double)numpt;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      size_t kj = 0;
+      for (auto it=start; it!=end; ++it, ++kj)
+	{
+	  Vector3D curr = (*it)->getPoint();
+	  A1[kj][0] = 2*curr[0];
+	  A1[kj][1] = 2*curr[1];
+	  A1[kj][2] = 2*curr[2];
+	  A1[kj][3] = 1.0;
+	  b1[kj] = curr.length2();
+	}
+    }
+
+  for (int ka=0; ka<4; ++ka)
+    {
+      for (int kb=0; kb<4; ++kb)
+	{
+	  for (size_t kr=0; kr<numpt; ++kr)
+	    Amat[ka][kb] += wgt*A1[kr][ka]*A1[kr][kb];
+	}
+      for (size_t kr=0; kr<numpt; ++kr)
+	bvec[ka] += wgt*A1[kr][ka]*b1[kr];
+    }
+
+  // double detA = 0.0;
+  // double bx[3];
+  // bx[0] = bx[1] = bx[2] = 0.0;
+  // int sgn1 = 1, sgn2 = 1;
+  // for (int kb=0; kb<4; ++kb, sgn1*=(-1))
+  //   {
+  //     for (int kc=0; kc<4; ++kc)
+  // 	{
+  // 	  if (kc == kb)
+  // 	    continue;
+  // 	  int ka1 = (kb == 0);
+  // 	  int ka2 = 3 - (kb == 3);
+  // 	  detA += sgn1*Amat[0][kb]*
+  // 	    (sgn2*Amat[1][kc]*(Amat[2][ka1]*Amat[3][ka2]-
+  // 			       Amat[3][ka1]*Amat[2][ka2]));
+  // 	  bx[0] += sgn1*bvec[kb]*
+  // 	    (sgn2*Amat[1][kc]*(Amat[2][ka1]*Amat[3][ka2]-
+  // 			       Amat[3][ka1]*Amat[2][ka2]));
+  // 	  bx[1] += sgn1*Amat[0][kb]*
+  // 	    (sgn2*bvec[kc]*(Amat[2][ka1]*Amat[3][ka2]-
+  // 			    Amat[3][ka1]*Amat[2][ka2]));
+  // 	  bx[2] += sgn1*Amat[0][kv]*
+  // 	    (sgn2*Amat[1][kc]*(bvec[ka1]Amat[3][ka2]-
+  // 			       bvec[ka2]*Amat[3][ka1]));
+  // 	  bx[3] += sgn1*Amat[0][kb]*
+  // 	    sgn2*Amat[1][kc]*((Amat[2][ka1]*bvec[ka2]-Amat[1][ka2]*bvec[ka1]);
+  // 	  sgn2 += -1;
+  // 	}
+  //   }
+  // double sx = bx[0]/detA;
+  // double sy = bx[1]/detA;
+  // double sz = bx[2]/detA;
+  // double r2 = bx[3]/detA;
+  LUsolveSystem(Amat, 4, &bvec[0]);
+  double sx = bvec[0];
+  double sy = bvec[1];
+  double sz = bvec[2];
+  double r2 = bvec[3];
+
+  centre = Point(sx,sy,sz);
+
+  radius = (r2 + sx*sx + sy*sy + sz*sz < 0.0) ? 0.0 : sqrt(r2 + sx*sx + sy*sy + sz*sz);
+ }
+
+//===========================================================================
+void RevEngUtils::computeCylPosRadius(vector<pair<vector<RevEngPoint*>::iterator,
+				      vector<RevEngPoint*>::iterator> >& points,
+				      Point& low, Point& high,
+				      Point& axis, Point& Cx, Point& Cy,
+				      Point& pos, double& radius)
+//===========================================================================
+{
+  double Amat[3][3];
+  double bvec[3];
+  size_t numpt = 0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      numpt += (points[ki].second - points[ki].first);
+    }
+  Point mid = 0.5*(low+high);
+  
+  vector<vector<double> > A1(numpt);
+  vector<double> b1(numpt);
+  for (size_t kj=0; kj<numpt; ++kj)
+    A1[kj].resize(3);
+      
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	Amat[ka][kb] = 0.0;
+      bvec[ka] = 0.0;
+    }
+
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      size_t kj = 0;
+      for (auto it=start; it!=end; ++it, ++kj)
+	{
+	  Vector3D curr = (*it)->getPoint();
+	  Point curr2(curr[0], curr[1], curr[2]);
+	  curr2 -= mid;
+	  double pxy[3];
+	  double px = curr2*Cx;
+	  double py = curr2*Cy;
+	  pxy[0] = 2*px;
+	  pxy[1] = 2*py;
+	  pxy[2] = 1.0;
+	  double plen2 = px*px + py*py;
+	  A1[kj][0] = 2*px;
+	  A1[kj][1] = 2*py;
+	  A1[kj][2] = 1.0;
+	  b1[kj] = plen2;
+	  for (int ka=0; ka<3; ++ka)
+	    {
+	      for (int kb=0; kb<3; ++kb)
+		Amat[ka][kb] += pxy[ka]*pxy[kb];
+	      bvec[ka] += pxy[ka]*plen2;
+	    }
+	}
+    }
+
+  double Amat2[3][3];
+  double bvec2[3];
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	Amat2[ka][kb] = 0.0;
+      bvec2[ka] = 0.0;
+    }
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	{
+	  for (size_t kr=0; kr<numpt; ++kr)
+	    Amat2[ka][kb] += A1[kr][ka]*A1[kr][kb];
+	}
+      for (size_t kr=0; kr<numpt; ++kr)
+	bvec2[ka] += A1[kr][ka]*b1[kr];
+    }
+
+  double detA = 0.0;
+  double bx[3];
+  bx[0] = bx[1] = bx[2] = 0.0;
+  int sgn = 1;
+  for (int kb=0; kb<3; ++kb, sgn*=(-1))
+    {
+      int ka1 = (kb == 0);
+      int ka2 = 2 - (kb == 2);
+      detA += sgn*Amat[0][kb]*(Amat[1][ka1]*Amat[2][ka2]-Amat[2][ka1]*Amat[1][ka2]);
+      bx[0] += sgn*bvec[kb]*(Amat[1][ka1]*Amat[2][ka2]-Amat[2][ka1]*Amat[1][ka2]);
+      bx[1] += sgn*Amat[0][kb]*(bvec[ka1]*Amat[2][ka2]-bvec[ka2]*Amat[2][ka1]);
+      bx[2] += sgn*Amat[0][kb]*(Amat[1][ka1]*bvec[ka2]-Amat[1][ka2]*bvec[ka1]);
+    }
+  double sx = bx[0]/detA;
+  double sy = bx[1]/detA;
+  double r2 = bx[2]/detA;
+
+  Point pos2 = sx*Cx + sy*Cy;
+  pos2 += mid;
+
+  radius = (r2 + sx*sx + sy*sy < 0.0) ? 0.0 : sqrt(r2 + sx*sx + sy*sy);
+  double len = low.dist(high);
+  Point vec = mid - pos2;
+  Point ax = axis;
+  ax.normalize();
+  pos = pos2 + (vec*ax)*ax;
+ }
+
+ 
+
+//===========================================================================
+void RevEngUtils::computeCircPosRadius(vector<Point>& points,
+				      const Point& axis, const Point& Cx, 
+				      const Point& Cy, Point& pos, double& radius)
+//===========================================================================
+{
+  double Amat[3][3];
+  double bvec[3];
+  size_t numpt = points.size();
+  double wgt = 1.0/(double)numpt;
+  
+  vector<vector<double> > A1(numpt);
+  vector<double> b1(numpt);
+  for (size_t kj=0; kj<numpt; ++kj)
+    A1[kj].resize(3);
+      
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	Amat[ka][kb] = 0.0;
+      bvec[ka] = 0.0;
+    }
+
+  Point mid(0.0, 0.0, 0.0);
+  for (size_t ki=0; ki<points.size(); ++ki)
+      mid += wgt*points[ki];
+  
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double pxy[3];
+      double px = (points[ki]-mid)*Cx;
+      double py = (points[ki]-mid)*Cy;
+      pxy[0] = 2*px;
+      pxy[1] = 2*py;
+      pxy[2] = 1.0;
+      double plen2 = px*px + py*py;
+      A1[ki][0] = 2*px;
+      A1[ki][1] = 2*py;
+      A1[ki][2] = 1.0;
+      b1[ki] = plen2;
+      for (int ka=0; ka<3; ++ka)
+	{
+	  for (int kb=0; kb<3; ++kb)
+	    Amat[ka][kb] += pxy[ka]*pxy[kb];
+	  bvec[ka] += pxy[ka]*plen2;
+	}
+    }
+
+  double Amat2[3][3];
+  double bvec2[3];
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	Amat2[ka][kb] = 0.0;
+      bvec2[ka] = 0.0;
+    }
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	{
+	  for (size_t kr=0; kr<numpt; ++kr)
+	    Amat2[ka][kb] += A1[kr][ka]*A1[kr][kb];
+	}
+      for (size_t kr=0; kr<numpt; ++kr)
+	bvec2[ka] += A1[kr][ka]*b1[kr];
+    }
+
+  double detA = 0.0;
+  double bx[3];
+  bx[0] = bx[1] = bx[2] = 0.0;
+  int sgn = 1;
+  for (int kb=0; kb<3; ++kb, sgn*=(-1))
+    {
+      int ka1 = (kb == 0);
+      int ka2 = 2 - (kb == 2);
+      detA += sgn*Amat[0][kb]*(Amat[1][ka1]*Amat[2][ka2]-Amat[2][ka1]*Amat[1][ka2]);
+      bx[0] += sgn*bvec[kb]*(Amat[1][ka1]*Amat[2][ka2]-Amat[2][ka1]*Amat[1][ka2]);
+      bx[1] += sgn*Amat[0][kb]*(bvec[ka1]*Amat[2][ka2]-bvec[ka2]*Amat[2][ka1]);
+      bx[2] += sgn*Amat[0][kb]*(Amat[1][ka1]*bvec[ka2]-Amat[1][ka2]*bvec[ka1]);
+    }
+  if (fabs(detA) < 1.0e-12 || std::isnan(detA))
+    THROW("Circle computation fail");
+  //std::cout << "Circposradius, detA:" << detA << std::endl;
+    
+  double sx = bx[0]/detA;
+  double sy = bx[1]/detA;
+  double r2 = bx[2]/detA;
+
+  pos = sx*Cx + sy*Cy;
+  pos += mid;
+  pos -= ((pos-mid)*axis)*axis;
+
+  radius = (r2 + sx*sx + sy*sy < 0.0) ? 0.0 : sqrt(r2 + sx*sx + sy*sy);
+ }
+
+
+
+//===========================================================================
+void RevEngUtils::computeRadius(vector<Point>& points, Point& axis, 
+				Point& Cx, Point& Cy, double& radius)
+//===========================================================================
+{
+  double Amat[3][3];
+  double bvec[3];
+  size_t numpt = points.size();
+  double wgt = 1.0/numpt;
+  
+  vector<vector<double> > A1(numpt);
+  vector<double> b1(numpt);
+  for (size_t kj=0; kj<numpt; ++kj)
+    A1[kj].resize(3);
+      
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	Amat[ka][kb] = 0.0;
+      bvec[ka] = 0.0;
+    }
+
+  Point mid(0.0, 0.0, 0.0);
+  for (size_t ki=0; ki<points.size(); ++ki)
+      mid += wgt*points[ki];
+  
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double pxy[3];
+      double px = (points[ki]-mid)*Cx;
+      double py = (points[ki]-mid)*Cy;
+      pxy[0] = 2*px;
+      pxy[1] = 2*py;
+      pxy[2] = 1.0;
+      double plen2 = px*px + py*py;
+      A1[ki][0] = 2*px;
+      A1[ki][1] = 2*py;
+      A1[ki][2] = 1.0;
+      b1[ki] = plen2;
+      for (int ka=0; ka<3; ++ka)
+	{
+	  for (int kb=0; kb<3; ++kb)
+	    Amat[ka][kb] += pxy[ka]*pxy[kb];
+	  bvec[ka] += pxy[ka]*plen2;
+	}
+    }
+
+  double Amat2[3][3];
+  double bvec2[3];
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	Amat2[ka][kb] = 0.0;
+      bvec2[ka] = 0.0;
+    }
+  for (int ka=0; ka<3; ++ka)
+    {
+      for (int kb=0; kb<3; ++kb)
+	{
+	  for (size_t kr=0; kr<numpt; ++kr)
+	    Amat2[ka][kb] += A1[kr][ka]*A1[kr][kb];
+	}
+      for (size_t kr=0; kr<numpt; ++kr)
+	bvec2[ka] += A1[kr][ka]*b1[kr];
+    }
+
+  double detA = 0.0;
+  double bx[3];
+  bx[0] = bx[1] = bx[2] = 0.0;
+  int sgn = 1;
+  for (int kb=0; kb<3; ++kb, sgn*=(-1))
+    {
+      int ka1 = (kb == 0);
+      int ka2 = 2 - (kb == 2);
+      detA += sgn*Amat[0][kb]*(Amat[1][ka1]*Amat[2][ka2]-Amat[2][ka1]*Amat[1][ka2]);
+      bx[0] += sgn*bvec[kb]*(Amat[1][ka1]*Amat[2][ka2]-Amat[2][ka1]*Amat[1][ka2]);
+      bx[1] += sgn*Amat[0][kb]*(bvec[ka1]*Amat[2][ka2]-bvec[ka2]*Amat[2][ka1]);
+      bx[2] += sgn*Amat[0][kb]*(Amat[1][ka1]*bvec[ka2]-Amat[1][ka2]*bvec[ka1]);
+    }
+  if (fabs(detA) < 1.0e-12)
+    THROW("Circle with infinite radius");
+  
+  double sx = bx[0]/detA;
+  double sy = bx[1]/detA;
+  double r2 = bx[2]/detA;
+
+  radius = (r2 + sx*sx + sy*sy < 0.0) ? 0.0 : sqrt(r2 + sx*sx + sy*sy);
+ }
+
+
+//===========================================================================
+void RevEngUtils::computePlane(vector<Point>& points, Point normal,
+			       Point mainaxis[3],
+			       Point& pos, Point& norm, Point& Cx, Point& Cy)
+//===========================================================================
+{
+  Point pos0(0.0, 0.0, 0.0);
+  double wgt = 1.0/(double)(points.size());
+  for (size_t ki=0; ki<points.size(); ++ki)
+    pos0 += wgt*points[ki];
+  
+  ImplicitApprox impl;
+  impl.approxPoints(points, 1);
+  bool found = impl.projectPoint(pos0, normal, pos, norm);
+  if (!found)
+    {
+      double eps = 1.0e-4;
+      double ang_min = 0.25*M_PI;
+      pos = pos0;
+      Point vec = points[0] - pos;
+      norm = Point(0.0, 0.0, 0.0);
+      size_t ki, kj;
+      for (ki=1; ki<points.size(); ++ki)
+	{
+	  if (vec.length() > eps)
+	    break;
+	  vec = points[ki] - pos;
+	  for (kj=ki+1; kj<points.size(); ++kj)
+	    {
+	      Point vec2 = points[kj] - pos;
+	      double ang = vec.angle(vec2);
+	      if (ang >= ang_min)
+		{
+		  Point norm2 = vec.cross(vec2);
+		  if (normal*norm2 < 0.0)
+		    norm2 *= -1;
+		  norm2.normalize();
+		  norm += norm2;
+		  break;
+		}
+	    }
+	}
+      norm.normalize();
+    }
+  if (normal*norm < 0.0)
+    norm *= -1.0;
+
+  // Define x-axis
+  int ix = -1;
+  double minang = M_PI;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = mainaxis[ka].angle(norm);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < minang)
+	{
+	  minang = ang;
+	  ix = ka;
+	}
+    }
+
+  Cy = mainaxis[(ix+1)%3].cross(norm);
+  Cx = norm.cross(Cy);
+}
+
+//===========================================================================
+void RevEngUtils::computeLine(vector<Point>& points, Point& pos, Point& dir)
+//===========================================================================
+{
+  size_t num = points.size();
+  pos = Point(0.0, 0.0, 0.0);
+  dir = Point(0.0, 0.0, 0.0);
+  if (num == 0)
+    return;
+  
+  double fac = 1.0/(double)num;
+  BoundingBox bb(3);
+  for (size_t ki=0; ki<num; ++ki)
+    {
+      pos += fac*points[ki];
+      bb.addUnionWith(points[ki]);
+    }
+
+  Point dir0 = bb.high() - bb.low();
+  for (size_t ki=0; ki<num; ++ki)
+    {
+      Point tmp = points[ki] - pos;
+      (void)tmp.normalize_checked();
+      if (tmp*dir0 < 0.0)
+	tmp *= -1;
+      dir += fac*tmp;
+    }
+  (void)dir.normalize_checked();
+}
+
+//===========================================================================
+void RevEngUtils::projectToPlane(vector<RevEngPoint*>& points,
+				 Point& axis, Point& mid, std::vector<Point>& projected,
+				 double& maxdist, double& avdist, double dlen)
+//===========================================================================
+{
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > points2;
+  points2.push_back(std::make_pair(points.begin(), points.end()));
+  projectToPlane(points2, axis, mid, projected, maxdist, avdist, dlen);
+}
+
+//===========================================================================
+void RevEngUtils::projectToPlane(std::vector<std::pair<std::vector<RevEngPoint*>::iterator,
+				 std::vector<RevEngPoint*>::iterator> >& points,
+				 Point& axis, Point& mid, std::vector<Point>& projected,
+				 double& maxdist, double& avdist, double dlen)
+//===========================================================================
+{
+  maxdist = 0.0;
+  avdist = 0.0;
+  int nmb = 0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      for (auto it=start; it!=end; ++it)
+	{
+	  RevEngPoint *pt = *it;
+	  Vector3D pnt = pt->getPoint();
+	  Point curr(pnt[0], pnt[1], pnt[2]);
+	  Point curr2 = curr - mid;
+	  double dd = curr2*axis;
+	  if (dlen > 0.0 && dd > dlen)
+	    continue;
+	  curr2 -= (dd*axis);
+	  curr2 += mid;
+	  projected.push_back(curr2);
+	  double dist = curr.dist(curr2);
+	  maxdist = std::max(maxdist, dist);
+	  avdist += dist;
+	  nmb++;
+	}
+    }
+  avdist /= (double)nmb;
+}
+
+//===========================================================================
+void RevEngUtils::rotateToPlane(vector<pair<vector<RevEngPoint*>::iterator,
+				vector<RevEngPoint*>::iterator> >& points,
+				Point& xvec, Point& axis, Point& mid,
+				vector<Point>& rotated)
+//===========================================================================
+{
+  Point yvec = xvec.cross(axis);
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      vector<RevEngPoint*>::iterator start = points[ki].first;
+      vector<RevEngPoint*>::iterator end = points[ki].second;
+      for (auto it=start; it!=end; ++it)
+	{
+	  RevEngPoint *pt = *it;
+	  Vector3D pnt = pt->getPoint();
+	  Point curr(pnt[0], pnt[1], pnt[2]);
+	  curr -= mid;
+	  pnt = Vector3D(curr[0], curr[1], curr[2]);
+	  curr -= (curr*axis)*axis;
+	  double angle = curr.angle(xvec);
+	  if (curr*yvec < 0.0)
+	    angle *= -1.0;
+	  Matrix3D mat;
+	  mat.setToRotation(angle, axis[0], axis[1], axis[2]);
+	  Vector3D pnt2 = mat*pnt;
+	  rotated.push_back(mid + Point(pnt2[0], pnt2[1], pnt2[2]));
+	}
+    }
+ }
+
+//===========================================================================
+void RevEngUtils::rotateToPlane(vector<Point>& points,
+				Point& xvec, Point& axis, Point& mid,
+				vector<Point>& rotated)
+//===========================================================================
+{
+  rotated.resize(points.size());
+  Point yvec = xvec.cross(axis);
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Point curr = points[ki];
+      curr -= mid;
+      Vector3D pnt(curr[0], curr[1], curr[2]);
+      curr -= (curr*axis)*axis;
+      double angle = curr.angle(xvec);
+      if (curr*yvec < 0.0)
+	angle *= -1.0;
+      Matrix3D mat;
+      mat.setToRotation(angle, axis[0], axis[1], axis[2]);
+      Vector3D pnt2 = mat*pnt;
+      rotated[ki] = mid + Point(pnt2[0], pnt2[1], pnt2[2]);
+    }
+}
+
+//===========================================================================
+void RevEngUtils::distToSurf(vector<RevEngPoint*>::iterator start,
+			     vector<RevEngPoint*>::iterator end,
+			     shared_ptr<ParamSurface> surf, double tol,
+			     double& maxdist, double& avdist,
+			     int& num_inside, int& num_inside2,
+			     vector<RevEngPoint*>& in, vector<RevEngPoint*>& out,
+			     vector<double>& parvals,
+			     vector<pair<double,double> >& distang,
+			     double angtol)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  maxdist = avdist = 0.0;
+  num_inside = num_inside2 = 0;
+  int num = (int)(end-start);
+  parvals.resize(2*num);
+  distang.resize(num);
+  in.clear();
+  out.clear();
+  double *seed = 0;
+  double seed2[2];
+  Point prev;
+  double fac = 100.0;
+  double upar, vpar, dist;
+  Point close;
+  Point norm1, norm2, norm3;
+  double ang, ang2;
+  double dfac = 1.0/(double)num;
+  size_t ki=0;
+  for (auto it=start; it!=end; ++it, ++ki)
+    {
+      Vector3D xyz = (*it)->getPoint();
+      Point pnt(xyz[0], xyz[1], xyz[2]);
+      if (prev.dimension() == pnt.dimension() && prev.dist(pnt) < fac*tol)
+	seed = seed2;
+      
+      surf->closestPoint(pnt, upar, vpar, close, dist, eps, 0, seed);
+      parvals[2*ki] = upar;
+      parvals[2*ki+1] = vpar;
+      surf->normal(norm1, upar, vpar);
+      norm2 = (*it)->getLocFuncNormal();
+      norm3 = (*it)->getTriangNormal();
+      maxdist = std::max(maxdist, dist);
+      avdist += dfac*dist;
+      ang = norm1.angle(norm2);
+      ang2 = norm1.angle(norm3);
+      ang = std::min(std::min(M_PI-ang, ang), std::min(M_PI-ang2,ang2));
+      distang[ki] = std::make_pair(dist, ang);
+      if (dist <= tol)
+	{
+	  ++num_inside2;
+	  if (angtol < 0.0 || ang <= angtol)
+	    {
+	      in.push_back(*it);
+		++num_inside;
+	    }
+	  else
+	    out.push_back(*it);
+	}
+      else
+	out.push_back(*it);
+      seed2[0] = upar;
+      seed2[1] = vpar;
+      prev = pnt;
+    }
+}
+
+//===========================================================================
+void RevEngUtils::distToSurf(vector<RevEngPoint*>::iterator start,
+			     vector<RevEngPoint*>::iterator end,
+			     shared_ptr<ParamSurface> surf, double tol,
+			     double& maxdist, double& avdist,
+			     int& num_inside, int& num_inside2,
+			     vector<double>& parvals,
+			     vector<pair<double,double> >& distang,
+			     double angtol)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  maxdist = avdist = 0.0;
+  num_inside = num_inside2 = 0;
+  int num = (int)(end-start);
+  parvals.resize(2*num);
+  distang.resize(num);
+  double *seed = 0;
+  double seed2[2];
+  Point prev;
+  double fac = 100.0;
+  double upar, vpar, dist;
+  Point close;
+  Point norm1, norm2, norm3;
+  double ang, ang2;
+  double dfac = 1.0/(double)num;
+  size_t ki=0;
+  for (auto it=start; it!=end; ++it, ++ki)
+    {
+      Vector3D xyz = (*it)->getPoint();
+      Point pnt(xyz[0], xyz[1], xyz[2]);
+      if (prev.dimension() == pnt.dimension() && prev.dist(pnt) < fac*tol)
+	seed = seed2;
+      
+      surf->closestPoint(pnt, upar, vpar, close, dist, eps, 0, seed);
+      parvals[2*ki] = upar;
+      parvals[2*ki+1] = vpar;
+      surf->normal(norm1, upar, vpar);
+      norm2 = (*it)->getLocFuncNormal();
+      norm3 = (*it)->getTriangNormal();
+      maxdist = std::max(maxdist, dist);
+      avdist += dfac*dist;
+      ang = norm1.angle(norm2);
+      ang2 = norm1.angle(norm3);
+      ang = std::min(std::min(M_PI-ang, ang), std::min(M_PI-ang2,ang2));
+      distang[ki] = std::make_pair(dist, ang);
+      if (dist <= tol)
+	{
+	  ++num_inside2;
+	  if (angtol < 0.0 || ang <= angtol)
+	    ++num_inside;
+	}
+      seed2[0] = upar;
+      seed2[1] = vpar;
+      prev = pnt;
+    }
+}
+
+//===========================================================================
+void RevEngUtils::distToSurf(vector<RevEngPoint*>& points,
+			     shared_ptr<ParamSurface> surf, double tol,
+			     double angtol, double& maxdist, double& avdist,
+			     int& inside, int& inside2,
+			     vector<pair<double,double> >& dist_ang)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  maxdist = avdist = 0.0;
+  inside = inside2 = 0;
+  int num = (int)points.size();
+  dist_ang.resize(num);
+  double *seed = 0;
+  double seed2[2];
+  Point prev;
+  double fac = 100.0;
+  double upar, vpar, dist;
+  Point close;
+  Point norm1, norm2, norm3;
+  double ang, ang2;
+  double dfac = 1.0/(double)num;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pnt(xyz[0], xyz[1], xyz[2]);
+      if (prev.dimension() == pnt.dimension() && prev.dist(pnt) < fac*tol)
+	seed = seed2;
+      
+      surf->closestPoint(pnt, upar, vpar, close, dist, eps, 0, seed);
+      surf->normal(norm1, upar, vpar);
+      norm2 = points[ki]->getLocFuncNormal();
+      norm3 = points[ki]->getTriangNormal();
+      maxdist = std::max(maxdist, dist);
+      avdist += dfac*dist;
+      ang = norm1.angle(norm2);
+      ang2 = norm1.angle(norm3);
+      ang = std::min(std::min(M_PI-ang, ang), std::min(M_PI-ang2,ang2));
+      dist_ang[ki] = std::make_pair(dist, ang);
+      if (dist <= tol)
+	{
+	  ++inside2;
+	  if (angtol < 0.0 || ang <= angtol)
+	    {
+	      ++inside;
+	    }
+	}
+      seed2[0] = upar;
+      seed2[1] = vpar;
+      prev = pnt;
+    }
+}
+
+//===========================================================================
+void RevEngUtils::distToSurf(vector<Point>& points,
+			     shared_ptr<ParamSurface> surf, double tol,
+			     double& maxdist, double& avdist, int& num_inside,
+			     vector<double>& distance)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  maxdist = avdist = 0.0;
+  num_inside = 0;
+  int num = (int)points.size();
+  distance.resize(num);
+  double fac = 1.0/(double)num;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double upar, vpar, dist;
+      Point close;
+      surf->closestPoint(points[ki], upar, vpar, close, dist, eps);
+      maxdist = std::max(maxdist, dist);
+      avdist += fac*dist;
+      distance[ki] = dist;
+      if (dist <= tol)
+	++num_inside;
+      else
+	{
+	  int stop_break = 1;
+	}
+    }
+}
+
+//===========================================================================
+void RevEngUtils::distToCurve(vector<Point>& points,
+			     shared_ptr<ParamCurve> curve, double tol,
+			     double& maxdist, double& avdist, int& num_inside)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  maxdist = avdist = 0.0;
+  num_inside = 0;
+  int num = (int)points.size();
+  double fac = 1.0/(double)num;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double tpar, dist;
+      Point close;
+      curve->closestPoint(points[ki], curve->startparam(), curve->endparam(),
+			  tpar, close, dist);
+      maxdist = std::max(maxdist, dist);
+      avdist += fac*dist;
+      if (dist <= tol)
+	++num_inside;
+      else
+	{
+	  int stop_break = 1;
+	}
+    }
+}
+
+
+//===========================================================================
+void RevEngUtils::distToCurve(vector<Point>& points,
+			      shared_ptr<ParamCurve> curve, double tol,
+			      double& maxdist, double& avdist, int& num_inside,
+			      vector<double>& dist)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  maxdist = avdist = 0.0;
+  num_inside = 0;
+  int num = 0;
+  dist.resize(points.size());
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double tpar;
+      Point close;
+      curve->closestPoint(points[ki], curve->startparam(), curve->endparam(),
+			  tpar, close, dist[ki]);
+      maxdist = std::max(maxdist, dist[ki]);
+      avdist += dist[ki];
+      if (dist[ki] <= tol)
+	++num_inside;
+      else
+	{
+	  int stop_break = 1;
+	}
+      ++num;
+    }
+  avdist /= (double)num;
+}
+
+//===========================================================================
+void RevEngUtils::distToCurve(vector<Point>& points,
+			      shared_ptr<ParamCurve> curve, double tol,
+			      double& maxdist, double& avdist, int& num_inside,
+			      vector<double>& parvals, vector<double>& dist)
+//===========================================================================
+{
+  double eps = 1.0e-6;
+  maxdist = avdist = 0.0;
+  num_inside = 0;
+  int num = 0;
+  parvals.resize(points.size());
+  dist.resize(points.size());
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double tpar;
+      Point close;
+      curve->closestPoint(points[ki], curve->startparam(), curve->endparam(),
+			  parvals[ki], close, dist[ki]);
+      maxdist = std::max(maxdist, dist[ki]);
+      avdist += dist[ki];
+      if (dist[ki] <= tol)
+	++num_inside;
+      else
+	{
+	  int stop_break = 1;
+	}
+      ++num;
+    }
+  avdist /= (double)num;
+}
+
+
+//===========================================================================
+shared_ptr<ElementarySurface>
+RevEngUtils::elemsurfWithAxis(shared_ptr<ElementarySurface> sf_in,
+			      vector<RevEngPoint*>& points,
+			      Point mainaxis[3], double diag)
+//===========================================================================
+{
+  Point axis = sf_in->direction();
+  Point loc = sf_in->location();
+  Point Cx = sf_in->direction2();
+  if (sf_in->instanceType() == Class_Plane)
+    return planeWithAxis(points, axis, loc, mainaxis);
+  else if (sf_in->instanceType() == Class_Cylinder)
+    return cylinderWithAxis(points, axis, Cx, loc);
+  else if (sf_in->instanceType() == Class_Torus)
+    return torusWithAxis(points, axis, Cx, loc);
+  else if (sf_in->instanceType() == Class_Sphere)
+    return sphereWithAxis(points, axis, Cx, loc);
+  else if (sf_in->instanceType() == Class_Cone)
+    return coneWithAxis(points, axis, Cx, loc, diag);
+  else
+    {
+      shared_ptr<ElementarySurface> dummy;
+      return dummy;
+    }
+}
+
+//===========================================================================
+shared_ptr<Plane> RevEngUtils::planeWithAxis(vector<RevEngPoint*>& points,
+					     Point axis, Point init_loc,
+					     Point mainaxis[3])
+//===========================================================================
+{
+  Point mid(0.0, 0.0, 0.0);
+  double wgt = 1.0/(double)points.size();
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D pos0 = points[ki]->getPoint();
+      Point pos(pos0[0], pos0[1], pos0[2]);
+      Point vec = pos - init_loc;
+      Point pos2 = init_loc + (vec*axis)*axis;
+      mid += wgt*pos2;
+    }
+
+  int ix=-1;
+  double min_ang = M_PI;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = axis.angle(mainaxis[ka]);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < min_ang)
+	{
+	  min_ang = ang;
+	  ix = ka;
+	}
+    }
+  shared_ptr<Plane> plane(new Plane(mid, axis, mainaxis[(ix+1)%3]));
+  return plane;
+}
+
+//===========================================================================
+shared_ptr<Cylinder> RevEngUtils::cylinderWithAxis(vector<RevEngPoint*>& points,
+						   Point axis, Point low, 
+						   Point high, Point mainaxis[3])
+//===========================================================================
+{
+  int ix=-1;
+  double min_ang = M_PI;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = axis.angle(mainaxis[ka]);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < min_ang)
+	{
+	  min_ang = ang;
+	  ix = ka;
+	}
+    }
+
+  Point Cx = mainaxis[(ix+2)%3].cross(axis);
+  Cx.normalize();
+  Point Cy = axis.cross(Cx);
+  Cy.normalize();
+
+  Point pos;
+  double rad;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  RevEngUtils::computeCylPosRadius(group, low, high, axis, Cx, Cy, pos, rad);
+
+  shared_ptr<Cylinder> cyl(new Cylinder(rad, pos, axis, Cy));
+  return cyl;
+}
+
+//===========================================================================
+shared_ptr<Cylinder> RevEngUtils::cylinderWithAxis(vector<RevEngPoint*>& points,
+						   Point axis, Point Cx,
+						   Point pos)
+//===========================================================================
+{
+  // Compute radius
+  double rad = 0.0;
+  double fac = 1.0/(double)points.size();
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pnt(xyz[0], xyz[1], xyz[2]);
+      Point pnt2 = pos + ((pnt - pos)*axis)*axis;
+      double dd = pnt.dist(pnt2);
+      rad += fac*dd;
+    }
+
+  shared_ptr<Cylinder> cyl(new Cylinder(rad, pos, axis, Cx));
+  return cyl;
+}
+
+//===========================================================================
+shared_ptr<Torus> RevEngUtils::torusWithAxis(vector<RevEngPoint*>& points,
+					     Point axis, Point loc, 
+					     Point mainaxis[3])
+//===========================================================================
+{
+  int ix=-1;
+  double min_ang = M_PI;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = axis.angle(mainaxis[ka]);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < min_ang)
+	{
+	  min_ang = ang;
+	  ix = ka;
+	}
+    }
+
+  Point Cx = mainaxis[(ix+2)%3].cross(axis);
+  Cx.normalize();
+  Point Cy = axis.cross(Cx);
+  Cy.normalize();
+
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  rotateToPlane(group, Cy, axis, loc, rotated);
+  
+  // Approximate rotated points with a circle
+  Point centre;
+  double radius;
+  computeCircPosRadius(rotated, Cx, Cy, axis, centre, radius);
+
+  Point axis_pt = loc + ((centre - loc)*axis)*axis;
+  double dist = centre.dist(axis_pt);
+  shared_ptr<Torus> torus(new Torus(dist, radius, axis_pt, axis, Cx));
+
+  return torus;
+ }
+
+//===========================================================================
+shared_ptr<Torus> RevEngUtils::torusWithAxis(vector<RevEngPoint*>& points,
+					     Point axis, Point Cx, Point pos)
+//===========================================================================
+{
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  Point Cy = axis.cross(Cx);
+  rotateToPlane(group, Cy, axis, pos, rotated);
+  
+  // Approximate rotated points with a circle
+  Point centre;
+  double radius;
+  computeCircPosRadius(rotated, Cx, Cy, axis, centre, radius);
+
+  Point axis_pt = pos + ((centre - pos)*axis)*axis;
+  double dist = centre.dist(axis_pt);
+  shared_ptr<Torus> torus(new Torus(dist, radius, axis_pt, axis, Cx));
+
+  return torus;
+ }
+
+//===========================================================================
+shared_ptr<Sphere> RevEngUtils::sphereWithAxis(vector<RevEngPoint*>& points,
+					       Point axis, 
+					       Point mainaxis[3])
+//===========================================================================
+{
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  Point centre;
+  double radius;
+  try {
+    computeSphereProp(group, centre, radius);
+  }
+  catch (...)
+    {
+      shared_ptr<Sphere> dummy;
+      return dummy;
+    }
+
+  // Define x-axis
+  int ix = -1;
+  double minang = M_PI;
+  for (int ka=0; ka<3; ++ka)
+    {
+      double ang = mainaxis[ka].angle(axis);
+      ang = std::min(ang, M_PI-ang);
+      if (ang < minang)
+	{
+	  minang = ang;
+	  ix = ka;
+	}
+    }
+
+  Point Cy = mainaxis[(ix+1)%3].cross(axis);
+  Point Cx = axis.cross(Cy);
+  
+  shared_ptr<Sphere> sph(new Sphere(radius, centre, axis, Cx));
+  return sph;
+}
+
+//===========================================================================
+shared_ptr<Sphere> RevEngUtils::sphereWithAxis(vector<RevEngPoint*>& points,
+						   Point axis, Point Cx,
+						   Point pos)
+//===========================================================================
+{
+  // Compute radius
+  double rad = 0.0;
+  double fac = 1.0/(double)points.size();
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      Point pnt(xyz[0], xyz[1], xyz[2]);
+      double dd = pnt.dist(pos);
+      rad += fac*dd;
+    }
+  
+  shared_ptr<Sphere> sph(new Sphere(rad, pos, axis, Cx));
+  return sph;
+}
+
+//===========================================================================
+shared_ptr<Cone> RevEngUtils::coneWithAxis(vector<RevEngPoint*>& points,
+					   Point axis, Point low, 
+					   Point high, Point mainaxis[3])
+//===========================================================================
+{
+  shared_ptr<Cylinder> cyl =
+    RevEngUtils::cylinderWithAxis(points, axis, low, high, mainaxis);
+  Point pnt = cyl->location();
+  Point Cx, Cy, Cz;
+  cyl->getCoordinateAxes(Cx, Cy, Cz);
+  //double rad = cyl->getRadius();
+
+#ifdef DEBUG_CONE
+  std::ofstream of("pts_cone.g2");
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << points.size() << std::endl;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    of << points[ki]->getPoint() << std::endl;
+#endif
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  RevEngUtils::rotateToPlane(group, Cy, axis, pnt, rotated);
+
+  double len = low.dist(high);
+  shared_ptr<Line> line(new Line(pnt, axis));
+  line->setParameterInterval(-len, len);
+  
+  Point pt1 = line->ParamCurve::point(-len);
+  Point pt2 = line->ParamCurve::point(len);
+  shared_ptr<SplineCurve> line_cv(new SplineCurve(pt1, -len, pt2, len));
+  shared_ptr<SplineCurve> cv1;
+  curveApprox(rotated, line_cv, 2, 2, cv1);
+
+  double tclose, dclose;
+  Point ptclose;
+  cv1->closestPoint(pnt, cv1->startparam(), cv1->endparam(), tclose,
+		    ptclose, dclose);
+  vector<Point> der(2);
+  cv1->point(der, tclose, 1);
+  double phi = der[1].angle(axis);
+  shared_ptr<Cone> cone(new Cone(dclose, pnt, axis, Cy, phi));
+
+#ifdef DEBUG_CONE
+  cone->writeStandardHeader(of);
+  cone->write(of);
+#endif
+  return cone;
+}
+
+//===========================================================================
+shared_ptr<Cone> RevEngUtils::coneWithAxis(vector<RevEngPoint*>& points,
+					   Point axis, Point Cx, Point pos,
+					   double len)
+//===========================================================================
+{
+#ifdef DEBUG_CONE
+  std::ofstream of("pts_cone.g2");
+  of << "400 1 0 4 255 0 0 255" << std::endl;
+  of << points.size() << std::endl;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    of << points[ki]->getPoint() << std::endl;
+#endif
+  
+  vector<Point> rotated;
+  vector<pair<vector<RevEngPoint*>::iterator,
+	      vector<RevEngPoint*>::iterator> > group;
+  group.push_back(std::make_pair(points.begin(), points.end()));
+  RevEngUtils::rotateToPlane(group, Cx, axis, pos, rotated);
+
+  shared_ptr<Line> line(new Line(pos, axis));
+  line->setParameterInterval(-len, len);
+  
+  Point pt1 = line->ParamCurve::point(-len);
+  Point pt2 = line->ParamCurve::point(len);
+  shared_ptr<SplineCurve> line_cv(new SplineCurve(pt1, -len, pt2, len));
+  shared_ptr<SplineCurve> cv1;
+  curveApprox(rotated, line_cv, 2, 2, cv1);
+#ifdef DEBUG_CONE
+  cv1->writeStandardHeader(of);
+  cv1->write(of);
+#endif
+
+  // Expects one intersection point with plane through point-on-axis
+  shared_ptr<Cone> cone;
+  double eps = 1.0e-6;
+  vector<double> intpar;
+  vector<pair<double,double> > intcvs;
+  double tpar;
+  intersectCurvePlane(cv1.get(), pos, axis, eps, intpar, intcvs);
+  if (intpar.size() > 0)
+    tpar = intpar[0];
+  else if (intcvs.size() > 0)
+    tpar = 0.5*(intcvs[0].first + intcvs[0].second);
+  else
+    return cone;
+
+  vector<Point> der(2);
+  cv1->point(der, tpar, 1);
+  double phi = der[1].angle(axis);
+  double rad = der[0].dist(pos);
+  cone = shared_ptr<Cone>(new Cone(rad, pos, axis, Cx, phi));
+
+#ifdef DEBUG_CONE
+  if (cone.get())
+    {
+      cone->writeStandardHeader(of);
+      cone->write(of);
+    }
+#endif
+  return cone;
+}
+
+//===========================================================================
+shared_ptr<ParamSurface> RevEngUtils::doMergePlanes(vector<pair<vector<RevEngPoint*>::iterator,
+						    vector<RevEngPoint*>::iterator> > points,
+						    const BoundingBox& bbox,
+						    vector<int>& nmbpts,
+						    bool set_bound)
+//===========================================================================
+{
+  int totnmb = 0;
+  for (size_t kh=0; kh<nmbpts.size(); ++kh)
+    totnmb += nmbpts[kh];
+  
+  Point pos(0.0, 0.0, 0.0);
+  Point norm(0.0, 0.0, 0.0);
+  vector<RevEngPoint*> all_pts;
+  all_pts.reserve(totnmb);
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double wgt = 1.0/(double)totnmb;
+
+      for (auto it=points[ki].first; it!=points[ki].second; ++it)
+	{
+	  Point curr = (*it)->getLocFuncNormal();
+	  Vector3D xyz = (*it)->getPoint();
+	  pos +=  wgt*Point(xyz[0], xyz[1], xyz[2]);
+	  norm += wgt*curr;
+	  all_pts.push_back(*it);
+	}
+    }
+  
+  // Perform approximation with combined point set
+  ImplicitApprox impl;
+  impl.approx(points, 1);
+  Point pos2, normal2;
+  bool found = impl.projectPoint(pos, norm, pos2, normal2);
+  if (!found)
+    {
+      pos2 = pos;
+      normal2 = norm;
+      normal2.normalize();
+    }
+   // std::ofstream outviz("implsf_merge.g2");
+  // impl->visualize(all_pts, outviz);
+ 
+  shared_ptr<Plane> surf(new Plane(pos2, normal2));
+  Point low = bbox.low();
+  Point high = bbox.high();
+  if (set_bound)
+    {
+      double len = low.dist(high);
+      surf->setParameterBounds(-0.5*len, -0.5*len, 0.5*len, 0.5*len);
+    }
+
+  return surf;
+}
+
+//===========================================================================
+shared_ptr<ParamSurface> RevEngUtils::doMergeCylinders(vector<pair<vector<RevEngPoint*>::iterator,
+						    vector<RevEngPoint*>::iterator> > points,
+						    const BoundingBox& bbox,
+						    vector<int>& nmbpts,
+						    bool set_bound)
+//===========================================================================
+{
+  // Estimate cylinder axis
+  Point axis, Cx, Cy;
+  RevEngUtils::computeAxis(points, axis, Cx, Cy);
+
+  // Estimate radius and point on axis
+  double rad;
+  Point pnt;
+  Point low = bbox.low();
+  Point high = bbox.high();
+  RevEngUtils::computeCylPosRadius(points, low, high,
+				   axis, Cx, Cy, pnt, rad);
+  shared_ptr<Cylinder> surf(new Cylinder(rad, pnt, axis, Cy));
+  if (set_bound)
+    {
+      double len = low.dist(high);
+      surf->setParamBoundsV(-len, len);
+    }
+
+  return surf;
+}
+
+//===========================================================================
+shared_ptr<ParamSurface> RevEngUtils::doMergeSpheres(vector<pair<vector<RevEngPoint*>::iterator,
+						    vector<RevEngPoint*>::iterator> > points,
+						    const BoundingBox& bbox,
+						     vector<int>& nmbpts, Point& normal)
+//===========================================================================
+{
+  Point centre;
+  double radius;
+  try {
+    RevEngUtils::computeSphereProp(points, centre, radius);
+  }
+  catch (...)
+    {
+      shared_ptr<Sphere> dummy;
+      return dummy;
+    }
+
+  int totnmb = 0;
+  for (size_t kh=0; kh<nmbpts.size(); ++kh)
+    totnmb += nmbpts[kh];
+  vector<Point> pnts;
+  pnts.reserve(totnmb);
+  for (size_t ki=0; ki<points.size(); ++ki)
+    for (auto it=points[ki].first; it!=points[ki].second; ++it)
+      {
+	Vector3D xyz = (*it)->getPoint();
+	pnts.push_back(Point(xyz[0], xyz[1], xyz[2]));
+      }
+
+  double eigenvec[3][3];
+  double lambda[3];
+  Point eigen1, eigen2, eigen3;
+  RevEngUtils::principalAnalysis(pnts[0], pnts, lambda, eigenvec);
+  Point z_axis = Point(eigenvec[1][0], eigenvec[1][1], eigenvec[1][2]);
+  Point x_axis = normal.cross(z_axis);
+
+  shared_ptr<Sphere> sph(new Sphere(radius, centre, z_axis, normal));
+
+  return sph;
+}
+
+//===========================================================================
+shared_ptr<ParamSurface> RevEngUtils::doMergeTorus(vector<pair<vector<RevEngPoint*>::iterator,
+						   vector<RevEngPoint*>::iterator> > points,
+						   const BoundingBox& bbox,
+						   vector<int>& nmbpts)
+//===========================================================================
+{
+  shared_ptr<ParamSurface> dummy;
+  
+  int totnmb = 0;
+  for (size_t kh=0; kh<nmbpts.size(); ++kh)
+    totnmb += nmbpts[kh];
+  
+  // Compute mean curvature and initial point in plane
+  double k2mean = 0.0;
+  double wgt = 1.0/(double)totnmb;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      for (auto it=points[ki].first; it!=points[ki].second; ++it)
+	{
+	  double kmax = (*it)->maxPrincipalCurvature();
+	  k2mean += wgt*kmax;
+	}
+    }
+  double rd = 1.0/k2mean;
+  
+  vector<Point> centr(totnmb);
+  Point mid(0.0, 0.0, 0.0);
+  size_t kr = 0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      for (auto it=points[ki].first; it!=points[ki].second; ++it, ++kr)
+	{
+	  Point norm = (*it)->getLocFuncNormal();
+	  Vector3D xyz = (*it)->getPoint();
+	  Point xyz2(xyz[0], xyz[1], xyz[2]);
+	  centr[kr] = xyz2 + rd*norm;
+	  mid += wgt*centr[kr];
+	}
+    }
+  
+  ImplicitApprox impl;
+  impl.approxPoints(centr, 1);
+
+  double val;
+  Point grad;
+  impl.evaluate(mid, val, grad);
+  grad.normalize_checked();
+  Point pos, normal;
+  bool found = impl.projectPoint(mid, grad, pos, normal);
+  if (!found)
+    return dummy;
+  double eps1 = 1.0e-8;
+  if (normal.length() < eps1)
+    return dummy;
+  
+  Point Cx = centr[0] - mid;
+  Cx -= (Cx*normal)*normal;
+  Cx.normalize();
+  Point Cy = Cx.cross(normal);
+  
+  double rad;
+  Point pnt;
+  RevEngUtils::computeCircPosRadius(centr, normal, Cx, Cy, pnt, rad);
+  pnt -= ((pnt - pos)*normal)*normal;
+
+  vector<Point> rotated;
+  RevEngUtils::rotateToPlane(points, Cx, normal, pnt, rotated);
+  Point cpos;
+  double crad;
+  RevEngUtils::computeCircPosRadius(rotated, Cy, Cx, normal, cpos, crad);
+  Point cvec = cpos - pnt;
+  double R1 = (cvec - (cvec*normal)*normal).length();
+  double R2 = (cvec*normal)*normal.length();
+ 
+  shared_ptr<Torus> surf(new Torus(R1, crad, pnt+R2*normal, normal, Cy));
+
+  return surf;
+}
+
+
+//===========================================================================
+shared_ptr<SplineCurve> RevEngUtils::midCurve(shared_ptr<SplineCurve>& cv1,
+					      shared_ptr<SplineCurve>& cv2)
+//===========================================================================
+{
+  shared_ptr<SplineCurve> spl1(cv1->clone());
+  shared_ptr<SplineCurve> spl2(cv2->clone());
+
+  // Check orientation
+  Point pt1 = spl1->ParamCurve::point(spl1->startparam());
+  Point pt2 = spl1->ParamCurve::point(spl1->endparam());
+  Point pt3 = spl2->ParamCurve::point(spl2->startparam());
+  Point pt4 = spl2->ParamCurve::point(spl2->endparam());
+  double len1 = pt1.dist(pt3);
+  double len2 = pt1.dist(pt4);
+  if (len2 < len1)
+    spl2->reverseParameterDirection();
+
+  // Ensure same spline room
+  spl2->setParameterInterval(spl1->startparam(), spl1->endparam());
+
+  double tol = 1.0e-4;
+  vector<shared_ptr<SplineCurve> > curves(2);
+  curves[0] = spl1;
+  curves[1] = spl2;
+  GeometryTools::unifyCurveSplineSpace(curves, tol);
+
+  shared_ptr<SplineCurve> midcv = GeometryTools::curveSum(*curves[0], 0.5,
+							  *curves[1], 0.5);
+  return midcv;
+}
+
+
+//===========================================================================
+void  RevEngUtils::curveApprox(vector<Point>& points,
+			       shared_ptr<ParamCurve> cvin,
+			       int ik, int in, 
+			       shared_ptr<SplineCurve>& curve)
+//===========================================================================
+{
+  vector<double> pts;
+  vector<double> param;
+  double tmin = cvin->startparam();
+  double tmax = cvin->endparam();
+  double tmin2 = tmax;
+  double tmax2 = tmin;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double tpar, dist;
+      Point close;
+      cvin->closestPoint(points[ki], tmin, tmax, tpar, close, dist);
+      pts.insert(pts.end(), points[ki].begin(), points[ki].end());
+      param.push_back(tpar);
+      tmin2 = std::min(tmin2, tpar);
+      tmax2 = std::max(tmax2, tpar);
+    }
+
+  double tdel = (tmax2 - tmin2)/(double)(in - ik + 1);
+  vector<double> et(ik+in);
+  for (int ka=0; ka<ik; ++ka)
+    {
+      et[ka] = tmin2;
+      et[in+ka] = tmax2;
+    }
+  for (int ka=ik; ka<in; ++ka)
+    et[ka] = tmin2 + (ka-ik+1)*tdel;
+
+  vector<double> ecoef(3*in, 0.0);
+  shared_ptr<SplineCurve> cv(new SplineCurve(in, ik, &et[0], &ecoef[0], 3));
+
+  SmoothCurve smooth(3);
+  vector<int> cfn(in, 0);
+  vector<double> wgts(param.size(), 1.0);
+  smooth.attach(cv, &cfn[0]);
+
+  smooth.setOptim(0.0, 0.001, 0.001);
+  smooth.setLeastSquares(pts, param, wgts, 0.998);
+
+  smooth.equationSolve(curve);
+  int stop_break = 1;
+}
+
+//===========================================================================
+void  RevEngUtils::curveApprox(vector<Point>& points,
+			       vector<double>& param,
+			       int ik, int in, 
+			       shared_ptr<SplineCurve>& curve)
+//===========================================================================
+{
+  if (points.size() == 0)
+    return;
+  if (points.size() != param.size())
+    return;
+  vector<double> pts;
+  double tmin = std::numeric_limits<double>::max();
+  double tmax = std::numeric_limits<double>::lowest();
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      pts.insert(pts.end(), points[ki].begin(), points[ki].end());
+      tmin = std::min(tmin, param[ki]);
+      tmax = std::max(tmax, param[ki]);
+    }
+
+  double tdel = (tmax - tmin)/(double)(in - ik + 1);
+  vector<double> et(ik+in);
+  for (int ka=0; ka<ik; ++ka)
+    {
+      et[ka] = tmin;
+      et[in+ka] = tmax;
+    }
+  for (int ka=ik; ka<in; ++ka)
+    et[ka] = tmin + (ka-ik+1)*tdel;
+
+  vector<double> ecoef(3*in, 0.0);
+  shared_ptr<SplineCurve> cv(new SplineCurve(in, ik, &et[0], &ecoef[0], 3));
+
+  SmoothCurve smooth(3);
+  vector<int> cfn(in, 0);
+  vector<double> wgts(param.size(), 1.0);
+  smooth.attach(cv, &cfn[0]);
+
+  smooth.setOptim(0.0, 0.001, 0.001);
+  smooth.setLeastSquares(pts, param, wgts, 0.998);
+
+  smooth.equationSolve(curve);
+  int stop_break = 1;
+}
+
+//===========================================================================
+shared_ptr<SplineCurve> RevEngUtils::createCurve(vector<RevEngPoint*>& points,
+						 int degree, double tol, int maxiter)
+//===========================================================================
+{
+  shared_ptr<SplineCurve> cv;
+  if (points.size() < 2)
+    return cv;
+  
+  // Parameterize curves and fetch data points
+  vector<double> param(points.size(), 0.0);
+  vector<double> pts;
+  pts.reserve(3*points.size());
+  Vector3D prev = points[0]->getPoint();
+  double tmp = 0.0;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      Vector3D xyz = points[ki]->getPoint();
+      pts.insert(pts.end(), xyz.begin(), xyz.end());
+      param[ki] = tmp + prev.dist(xyz);
+      prev = xyz;
+      tmp = param[ki];
+    }
+
+  double smoothwgt = 0.1;
+  ApproxCurve approx(pts, param, 3, tol, degree+1, degree+1);
+  approx.setSmooth(smoothwgt);
+
+  double maxdist, avdist;
+  cv = approx.getApproxCurve(maxdist, avdist, maxiter);
+
+  return cv;
+}
+
+//===========================================================================
+void RevEngUtils::extractLinearPoints(vector<RevEngPoint*>& points,
+				      vector<Point>& rotated, double len,
+				      Point& pos, Point& axis, double rad,
+				      Point& axis2, bool plane,
+				      double tol, double angtol,
+				      vector<pair<double,double> >& dist_ang,
+				      vector<RevEngPoint*>& linear, bool start,
+				      vector<RevEngPoint*>& remaining)
+//===========================================================================
+{
+  // Parametarize the points according to axis
+  vector<double> param(points.size());
+  vector<double> distance(points.size());
+  vector<int> perm(points.size());
+  shared_ptr<Line> line(new Line(pos, axis));
+  line->setParameterInterval(-len, len);
+
+  double tmin = -len;
+  double tmax = len;
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double tpar, dist;
+      Point close;
+      line->closestPoint(rotated[ki], -len, len, tpar, close, dist);
+      param[ki] = tpar;
+      distance[ki] = dist;
+      perm[ki] = (int)ki;
+      tmin = std::min(tmin, tpar);
+      tmax = std::max(tmax, tpar);
+    }
+
+  // Sort
+  for (size_t ki=0; ki<perm.size(); ++ki)
+    for (size_t kj=ki+1; kj<perm.size(); ++kj)
+      if (param[perm[kj]] < param[perm[ki]])
+	std::swap(perm[ki], perm[kj]);
+
+  // Identify linear points
+  double pihalf = 0.5*M_PI;
+  int ix1 = (start) ? 0 : (int)perm.size()-1;
+  int ix2 = (start) ? (int)perm.size() : -1;
+  int sgn = (start) ? 1 : -1;
+  int lim1 = std::min(10, (int)points.size()/200);
+  int lim2 = std::min(100, (int)points.size()/50);
+  int ka, kb;
+  int num_out = 0;
+  double fac = 0.9;
+  double dfac = 0.5;
+  for (ka=ix1; ka!=ix2; ka=kb)
+    {
+      Point norm = points[perm[ka]]->getLocFuncNormal();
+      Point norm2 = points[perm[ka]]->getTriangNormal();
+      double ang = norm.angle(axis2);
+      double ang2 = norm2.angle(axis2);
+      if (plane)
+	ang = std::min(std::min(ang,M_PI-ang), std::min(ang2,M_PI-ang2));
+      else
+	ang = std::min(fabs(pihalf-ang), fabs(pihalf-ang2));
+      double dd = fabs(distance[perm[ka]]-rad);
+      double anglim = (dist_ang.size() > 0) ? dist_ang[perm[ka]].second : 0.0;
+      double angtol2 = std::max(fac*anglim, angtol);
+      double tol2 = (dist_ang.size() > 0) ?
+	std::max(tol, dfac*dist_ang[perm[ka]].first) : tol;;
+      if (dd <= tol2 && ang <= angtol2)
+	kb = ka+sgn;
+      else
+	{
+	  num_out++;
+	  for (kb=ka+sgn; kb!=ix2; kb+=sgn)
+	    {
+	      norm = points[perm[kb]]->getLocFuncNormal();
+	      norm2 = points[perm[kb]]->getTriangNormal();
+	      ang = norm.angle(axis2);
+	       ang2 = norm2.angle(axis2);
+	      ang = std::min(fabs(pihalf-ang), fabs(pihalf-ang2));
+	      dd = fabs(distance[perm[kb]]-rad);
+	      anglim = (dist_ang.size() > 0) ? dist_ang[perm[kb]].second : 0.0;
+	      angtol2 = std::max(fac*anglim, angtol);
+	      tol2 = (dist_ang.size() > 0) ?
+		std::max(tol, dfac*dist_ang[perm[kb]].first) : tol;;
+	      if (dd <= tol2 && ang <= angtol2)
+		break;
+	      num_out++;
+	      if (abs(kb-ka) > lim1 || num_out > lim2)
+		break;
+	    }
+	}
+      if (abs(kb-ka) > lim1 || num_out > lim2)
+	break;
+    }
+
+  // Extract linear points
+  if (ka == ix2)
+    ka+=sgn;
+  for (kb=ix1; kb!=ka && kb<(int)perm.size() && kb>=0; kb+=sgn)
+    linear.push_back(points[perm[kb]]);
+  for (; kb!=ix2 && kb<(int)perm.size() && kb>=0; kb+=sgn)
+    remaining.push_back(points[perm[kb]]);
+  int stop_break = 1;
+}
+
+struct LinInfo
+{
+  double t1_, t2_;
+  double mind_, maxd_, avd_, avd2_;
+  int nmb_;
+
+  LinInfo()
+  {
+    t1_ = t2_ = mind_ = maxd_ = avd_ = avd2_ = 0.0;
+    nmb_ = 0;
+  }
+
+  LinInfo(double t1, double t2, int nmb, double mind, double maxd,
+	  double avd, double avd2)
+  {
+    t1_ = t1;
+    t2_ = t2;
+    nmb_ = nmb;
+    mind_ = mind;
+    maxd_ = maxd;
+    avd_ = avd;
+    avd2_ = avd2;
+  }
+};
+
+//===========================================================================
+bool RevEngUtils::extractLinearPoints(vector<Point>& points,
+				      shared_ptr<Line>& line, Point norm,
+				      double tol, bool start, double& splitpar,
+				      vector<Point>& linear, 
+				      vector<Point>& remaining)
+//===========================================================================
+{
+  // Parametarize the points according to axis
+  vector<double> param(points.size());
+  vector<double> distance(points.size());
+  vector<int> perm(points.size());
+
+  double tmin = line->startparam();
+  double tmax = line->endparam();
+  for (size_t ki=0; ki<points.size(); ++ki)
+    {
+      double tpar, dist;
+      Point close;
+      line->closestPoint(points[ki], line->startparam(), line->endparam(),
+			 tpar, close, dist);
+      param[ki] = tpar;
+      int sgn = ((close-points[ki])*norm > 0.0) ? 1 : -1;
+      distance[ki] = sgn*dist;
+      perm[ki] = (int)ki;
+      tmin = std::min(tmin, tpar);
+      tmax = std::max(tmax, tpar);
+    }
+
+  // Sort
+  for (size_t ki=0; ki<perm.size(); ++ki)
+    for (size_t kj=ki+1; kj<perm.size(); ++kj)
+      if (param[perm[kj]] < param[perm[ki]])
+	std::swap(perm[ki], perm[kj]);
+
+  int ndiv = 100;
+  double tdel = (tmax - tmin)/(double)ndiv;
+  vector<LinInfo> info(ndiv);
+  double t1, t2;
+  int ka;
+  size_t kj=0;
+  for (ka=0, t1=tmin, t2=t1+tdel; ka<ndiv; ++ka, t1+=tdel, t2+=tdel)
+    {
+      int nmb = 0;
+      double maxd = std::numeric_limits<double>::lowest();
+      double mind = std::numeric_limits<double>::max();
+      double avd = 0.0, avd2 = 0.0;
+      for (; kj<perm.size() && param[perm[kj]] <= t2; ++kj)
+	{
+	  avd += fabs(distance[perm[kj]]);
+	  avd2 += distance[perm[kj]];
+	  maxd = std::max(maxd, distance[perm[kj]]);
+	  mind = std::min(mind, distance[perm[kj]]);
+	  ++nmb;
+	}
+      if (nmb > 0)
+	{
+	  avd /= (double)nmb;
+	  avd2 /= (double)nmb;
+	}
+      else
+	{
+	  mind = maxd = avd = avd2 = 0.0;
+	}
+      LinInfo curr_info(t1, t2, nmb, mind, maxd, avd, avd2);
+      info[ka] = curr_info;
+    }
+
+  // Count number of information entities without any points
+  int num_zero = 0;
+  for (size_t ki=0; ki<info.size(); ++ki)
+    if (info[ki].nmb_ == 0)
+      num_zero++;
+
+  int zero_lim = ndiv/3;
+  if (num_zero > zero_lim)
+    return false;
+  
+  vector<double> par;
+  vector<double> range;
+  vector<double> avdist1;
+  vector<double> avdist2;
+  for (int ka=0; ka<ndiv; ++ka)
+    {
+      if (info[ka].nmb_ > 0)
+	{
+	  par.push_back(0.5*(info[ka].t1_ + info[ka].t2_));
+	  range.push_back(info[ka].maxd_ - info[ka].mind_);
+	  avdist1.push_back(info[ka].avd_);
+	  avdist2.push_back(info[ka].avd2_);
+	}
+    }
+
+  int in = 6;
+  int ik = 4;
+  double smoothwgt = 1.0e-5;
+  int maxiter = 4;
+  double tol2 = 1.0e-6;
+  double maxdist, avdist;
+  ApproxCurve approx2(avdist1, par, 1, tol2, in, ik);
+  shared_ptr<SplineCurve> cv2 = approx2.getApproxCurve(maxdist, avdist, maxiter);
+#ifdef DEBUG_BLEND
+  double maxdistd, avdistd;
+  ApproxCurve approx1(range, par, 1, tol2, in, ik);
+  shared_ptr<SplineCurve> cv1 = approx1.getApproxCurve(maxdistd, avdistd, maxiter);
+  ApproxCurve approx3(avdist2, par, 1, tol2, in, ik);
+  shared_ptr<SplineCurve> cv3 = approx3.getApproxCurve(maxdistd, avdistd, maxiter);
+  std::ofstream of("dist_cvs.g2");
+  SplineDebugUtils::writeSpace1DCurve(*cv1, of);
+  SplineDebugUtils::writeSpace1DCurve(*cv2, of);
+  SplineDebugUtils::writeSpace1DCurve(*cv3, of);
+  of << "410 1 0 0" << std::endl;
+  of << "1" << std::endl;
+  of << tmin << " 0 0 " << tmax << " 0 0 " << std::endl;
+  of << "410 1 0 0" << std::endl;
+  of << "1" << std::endl;
+  of << tmin << " " << tol << " 0 " << tmax << " " << tol << " 0 " << std::endl;
+  of << "410 1 0 0" << std::endl;
+  of << "1" << std::endl;
+  of << tmin << " " << 2*tol << " 0 " << tmax << " " << 2*tol << " 0 " << std::endl;
+  of << "410 1 0 0" << std::endl;
+  of << "1" << std::endl;
+  of << tmin << " " << -tol << " 0 " << tmax << " " << -tol << " 0 " << std::endl;
+
+  shared_ptr<SplineCurve> cv1_2(cv1->derivCurve(1));
+  shared_ptr<SplineCurve> cv2_2(cv2->derivCurve(1));
+  shared_ptr<SplineCurve> cv3_2(cv3->derivCurve(1));
+
+  Point zero(1);
+  zero[0] = 0.0;
+  vector<double> intpar1, intpar2, intpar3;
+  vector<pair<double,double> > intcv1, intcv2, intcv3;
+  intersectCurvePoint(cv1_2.get(), zero, tol2, intpar1, intcv1);
+  intersectCurvePoint(cv2_2.get(), zero, tol2, intpar2, intcv2);
+  intersectCurvePoint(cv3_2.get(), zero, tol2, intpar3, intcv3);
+ for (auto it=cv1_2->coefs_begin(); it != cv1_2->coefs_end(); ++it)
+    (*it) *= 0.01;
+  for (auto it=cv2_2->coefs_begin(); it != cv2_2->coefs_end(); ++it)
+    (*it) *= 0.01;
+  for (auto it=cv3_2->coefs_begin(); it != cv3_2->coefs_end(); ++it)
+    (*it) *= 0.01;
+  std::ofstream of2("dist_cvs_2.g2");
+  SplineDebugUtils::writeSpace1DCurve(*cv1_2, of2);
+  SplineDebugUtils::writeSpace1DCurve(*cv2_2, of2);
+  SplineDebugUtils::writeSpace1DCurve(*cv3_2, of2);
+#endif
+  // Set limitation of average distance for the line to be an accepted
+  // approximation
+  int asgn = (start) ? 1 : -1;
+  double av1 = 0.0, minav2=2.0*tol, maxav2=0.0;
+  int an = 0;
+  double afac = 2.0;
+  for (int ka=(start)?0:ndiv-1; ka!=ndiv/2; ka+=asgn)
+    {
+      double av2=0.0;
+      double fac = 0.1;
+      for (int kb=0; kb<10; ++kb)
+	av2 += fac*info[ka+asgn*kb].avd_;
+
+      if (an>0 && av2>afac*av1/(double)an)
+	break;
+      av1 += av2;
+      ++an;
+      minav2 = std::min(minav2, av2);
+      maxav2 = std::max(maxav2, av2);
+    }
+  if (an > 0)
+    av1 /= (double)an;
+  double av2del = maxav2 - minav2;
+  av2del = std::max(av2del, avdist);
+
+  splitpar = (start) ? tmin - tmax : 2*tmax;
+  vector<double> intpar, intpar_n;
+  vector<pair<double,double> > intcv, intcv_n;
+  Point tolpt(1);
+  double tolfac = 3;
+  double toldel = tolfac*av2del;
+  toldel = std::max(toldel, 0.25*tol);
+  tolpt[0] = std::min(av1 + toldel, tol);
+#ifdef DEBUG_BLEND
+  of << "410 1 0 0" << std::endl;
+  of << "1" << std::endl;
+  of << tmin << " " << tolpt << " 0 " << tmax << " " << tolpt << " 0 " << std::endl;
+  of << "410 1 0 0" << std::endl;
+  of << "1" << std::endl;
+  of << tmin << " " << av1-tolfac*av2del << " 0 " << tmax << " " << av1-tolfac*av2del << " 0 " << std::endl;
+#endif
+  if (an > 0)
+    {
+      intersectCurvePoint(cv2.get(), tolpt, tol2, intpar, intcv);
+      tolpt[0] = av1 - std::min(toldel, tol-av1);
+      intersectCurvePoint(cv2.get(), tolpt, tol2, intpar_n, intcv_n);
+      if (intpar_n.size() > 0)
+	intpar.insert(intpar.end(), intpar_n.begin(), intpar_n.end());
+    }
+      
+  if (intpar.size() > 0)
+    {
+      std::sort(intpar.begin(), intpar.end());
+      splitpar = (start) ? intpar[0] : intpar[intpar.size()-1];
+    }
+#ifdef DEBUG_BLEND  
+  else
+    {
+  //int nmbsplit = 0;
+  int splitsgn = (start) ? 1 : -1;
+  vector<Point> der(2);
+  for (size_t kr=0; kr<intpar1.size(); ++kr)
+    {
+      cv1_2->point(der, intpar1[kr], 1);
+      if (splitsgn*der[1][0] > 0.0)
+	{
+	  // splitpar += intpar1[kr];
+	  // nmbsplit++;
+	  splitpar = (start) ? std::max(splitpar, intpar1[kr]) :
+	    std::min(splitpar, intpar1[kr]);
+	}
+    }
+  for (size_t kr=0; kr<intpar2.size(); ++kr)
+    {
+      cv2_2->point(der, intpar2[kr], 1);
+      if (splitsgn*der[1][0] > 0.0)
+	{
+	  // splitpar += intpar2[kr];
+	  // nmbsplit++;
+	  splitpar = (start) ? std::max(splitpar, intpar2[kr]) :
+	    std::min(splitpar, intpar2[kr]);
+	}
+    }
+  for (size_t kr=0; kr<intpar3.size(); ++kr)
+    {
+      cv3_2->point(der, intpar3[kr], 1);
+      if (splitsgn*der[1][0] > 0.0)
+	{
+	  // splitpar += intpar3[kr];
+	  // nmbsplit++;
+	  splitpar = (start) ? std::max(splitpar, intpar3[kr]) :
+	    std::min(splitpar, intpar3[kr]);
+	}
+    }
+    }
+#endif
+  if (splitpar > tmin && splitpar < tmax) //nmbsplit > 0)
+    {
+      //splitpar /= (double)nmbsplit;
+      if (start)
+	{
+	  size_t kr;
+	  for (kr=0; kr<perm.size() && param[perm[kr]] <= splitpar; ++kr)
+	    linear.push_back(points[perm[kr]]);
+	  for (; kr<perm.size(); ++kr)
+	    remaining.push_back(points[perm[kr]]);
+	}
+      else
+	{
+	  size_t kr;
+	  for (kr=0; kr<perm.size() && param[perm[kr]] < splitpar; ++kr)
+	    remaining.push_back(points[perm[kr]]);
+	  for (; kr<perm.size(); ++kr)
+	    linear.push_back(points[perm[kr]]);
+	}
+    }
+  else
+    remaining.insert(remaining.end(), points.begin(), points.end());
+  
+  return true;
+}
+
+
+//===========================================================================
+void RevEngUtils::identifyEndPoints(vector<RevEngPoint*> edge_pts, shared_ptr<CurveOnSurface>& sfcv,
+				    RevEngPoint*& first_pt, double& t1,
+				    RevEngPoint*& last_pt, double& t2)
+//===========================================================================
+{
+  shared_ptr<ParamCurve> pcrv = sfcv->parameterCurve();
+  shared_ptr<ParamCurve> spacecrv = sfcv->spaceCurve();
+  bool parpref = sfcv->parPref();
+  t1 = sfcv->endparam();
+  t2 = sfcv->startparam();
+  double tpar, dist;
+  Point close;
+  double t3 = sfcv->startparam();
+  double t4 = sfcv->endparam();
+   if (pcrv && parpref)
+    {
+      for (size_t ki=0; ki<edge_pts.size(); ++ki)
+	{
+	  Vector2D uv = edge_pts[ki]->getPar();
+	  Point ppt(uv[0], uv[1]);
+	  pcrv->closestPoint(ppt, t3, t4, tpar, close, dist);
+	  if (tpar < t1)
+	    {
+	      t1 = tpar;
+	      first_pt = edge_pts[ki];
+	    }
+	  if (tpar > t2)
+	    {
+	      t2 = tpar;
+	      last_pt = edge_pts[ki];
+	    }
+	}
+    }
+  else
+    {
+      for (size_t ki=0; ki<edge_pts.size(); ++ki)
+	{
+	  Vector3D xyz = edge_pts[ki]->getPoint();
+	  Point ppt(xyz[0], xyz[1], xyz[2]);
+	  spacecrv->closestPoint(ppt, t3, t4, tpar, close, dist);
+	  if (tpar < t1)
+	    {
+	      t1 = tpar;
+	      first_pt = edge_pts[ki];
+	    }
+	  if (tpar > t2)
+	    {
+	      t2 = tpar;
+	      last_pt = edge_pts[ki];
+	    }
+	}
+     }
+}
+
+
+//===========================================================================
+void RevEngUtils::setLoopSeq(vector<shared_ptr<CurveOnSurface> >& cvs)
+//===========================================================================
+{
+  if (cvs.size() <= 1)
+    return;
+  
+  Point pos1 = cvs[0]->ParamCurve::point(cvs[0]->endparam());
+  for (size_t ki=1; ki<cvs.size(); ++ki)
+    {
+      Point pos2 = cvs[ki]->ParamCurve::point(cvs[ki]->startparam());
+      Point pos3 = cvs[ki]->ParamCurve::point(cvs[ki]->endparam());
+      double dd2 = pos1.dist(pos2);
+      double dd3 = pos1.dist(pos3);
+      for (size_t kj=ki+1; kj<cvs.size(); ++kj)
+	{
+	  Point pos4 = cvs[kj]->ParamCurve::point(cvs[kj]->startparam());
+	  Point pos5 = cvs[kj]->ParamCurve::point(cvs[kj]->endparam());
+	  double dd4 = pos1.dist(pos4);
+	  double dd5 = pos1.dist(pos5);
+	  if (std::min(dd4,dd5) < std::min(dd2,dd3))
+	    {
+	      std::swap(cvs[ki], cvs[kj]);
+	      std::swap(dd2, dd4);
+	      std::swap(dd3, dd5);
+	    }
+	}
+      if (dd3 < dd2)
+	cvs[ki]->reverseParameterDirection();
+      pos1 = cvs[ki]->ParamCurve::point(cvs[ki]->endparam());
+    }
+}
+
+
+//===========================================================================
+void RevEngUtils::identifyConGroups(vector<RevEngPoint*>& init,
+				    vector<vector<RevEngPoint*> >& groups)
+//===========================================================================
+{
+  // The points may belong to different regions and have different
+  // classifications. Mark to identify
+  int mark = 1;
+  for (size_t ki=0; ki<init.size(); ++ki)
+    init[ki]->setMarkIx(mark);
+
+  for (size_t ki=0; ki<init.size(); ++ki)
+    {
+      if (init[ki]->visited())
+	continue;
+      vector<RevEngPoint*> curr_group;
+      init[ki]->fetchConnectedMarked(mark, curr_group);
+       groups.push_back(curr_group);
+     }
+
+  for (size_t ki=0; ki<init.size(); ++ki)
+    {
+      init[ki]->unsetMarkIx();
+      init[ki]->unsetVisited();
+    }
+
+  // Sort groups according to size
+  for (size_t ki=0; ki<groups.size(); ++ki)
+    for (size_t kj=ki+1; kj<groups.size(); ++kj)
+      if (groups[kj].size() > groups[ki].size())
+	std::swap(groups[ki], groups[kj]);
+}
+
+
diff --git a/gotools-core/include/GoTools/geometry/streamable_doxymain.h b/gotools-core/include/GoTools/geometry/streamable_doxymain.h
index 79405f69..1795bd0d 100644
--- a/gotools-core/include/GoTools/geometry/streamable_doxymain.h
+++ b/gotools-core/include/GoTools/geometry/streamable_doxymain.h
@@ -37,8 +37,8 @@
  * written agreement between you and SINTEF ICT. 
  */
 
-#ifndef _PARAMETRIZATION_DOXYMAIN_H
-#define _PARAMETRIZATION_DOXYMAIN_H
+#ifndef _STREAMABLE_DOXYMAIN_H
+#define _STREAMABLE_DOXYMAIN_H
 
 /**
 \page streamable_doc The g2-format, GoTools file format for geometry entities
@@ -652,4 +652,4 @@ have degenerate corners (=0)
 flag for centre degeneracy is false.
 */
 
-#endif // _PARAMETRIZATION_DOXYMAIN_H
+#endif // _STREAMABLE_DOXYMAIN_H