/// <summary>
        /// Retrieve all plan view boundary loops from 
        /// all solids of given element united together.
        /// If the element is a family instance, transform
        /// its loops from the instance placement 
        /// coordinate system back to the symbol 
        /// definition one.
        /// If no geometry can be determined, use the 
        /// bounding box instead.
        /// </summary>
        static JtLoops GetPlanViewBoundaryLoops(
            Element e,
            ref int nFailures)
        {
            Autodesk.Revit.Creation.Application creapp
            = e.Document.Application.Create;

              JtLoops loops = null;

              Options opt = new Options();

              GeometryElement geo = e.get_Geometry( opt );

              if( null != geo )
              {
            Document doc = e.Document;

            if( e is FamilyInstance )
            {
              // Retrieve family instance geometry
              // transformed back to symbol definition
              // coordinate space by inverting the
              // family instance placement transformation

              LocationPoint lp = e.Location
            as LocationPoint;

              Transform t = Transform.CreateTranslation(
            -lp.Point );

              Transform r = Transform.CreateRotationAtPoint(
            XYZ.BasisZ, -lp.Rotation, lp.Point );

              geo = geo.GetTransformed( t * r );
            }

            loops = GetPlanViewBoundaryLoopsGeo(
              creapp, geo, ref nFailures );
              }
              if( null == loops || 0 == loops.Count )
              {
            Debug.Print(
              "Unable to determine geometry for "
              + Util.ElementDescription( e )
              + "; using bounding box instead." );

            BoundingBoxXYZ bb;

            if( e is FamilyInstance )
            {
              bb = ( e as FamilyInstance ).Symbol
            .get_BoundingBox( null );
            }
            else
            {
              bb = e.get_BoundingBox( null );
            }
            JtLoop loop = new JtLoop( 4 );
            loop.Add( new Point2dInt( bb.Min ) );
            loop.Add( new Point2dInt( bb.Max.X, bb.Min.Y ) );
            loop.Add( new Point2dInt( bb.Max ) );
            loop.Add( new Point2dInt( bb.Min.X, bb.Max.Y ) );
            loops.Add( loop );
              }
              return loops;
        }
        /// <summary>
        /// Retrieve the room plan view boundary 
        /// polygon loops and convert to 2D integer-based.
        /// For optimisation and consistency reasons, 
        /// convert all coordinates to integer values in
        /// millimetres. Revit precision is limited to 
        /// 1/16 of an inch, which is abaut 1.2 mm, anyway.
        /// </summary>
        static JtLoops GetRoomLoops( Room room )
        {
            SpatialElementBoundaryOptions opt
            = new SpatialElementBoundaryOptions();

              opt.SpatialElementBoundaryLocation =
            SpatialElementBoundaryLocation.Center; // loops closed
            //SpatialElementBoundaryLocation.Finish; // loops not closed

              IList<IList<BoundarySegment>> loops = room.
            GetBoundarySegments( opt );

              int nLoops = loops.Count;

              JtLoops jtloops = new JtLoops( nLoops );

              foreach( IList<BoundarySegment> loop in loops )
              {
            int nSegments = loop.Count;

            JtLoop jtloop = new JtLoop( nSegments );

            XYZ p0 = null; // loop start point
            XYZ p; // segment start point
            XYZ q = null; // segment end point

            foreach( BoundarySegment seg in loop )
            {
              // Todo: handle non-linear curve.
              // Especially: if two long lines have a
              // short arc in between them, skip the arc
              // and extend both lines.

              p = seg.GetCurve().GetEndPoint( 0 );

              jtloop.Add( new Point2dInt( p ) );

              Debug.Assert( null == q || q.IsAlmostEqualTo( p ),
            "expected last endpoint to equal current start point" );

              q = seg.GetCurve().GetEndPoint( 1 );

              if( _debug_output )
              {
            Debug.Print( "{0} --> {1}",
              Util.PointString( p ),
              Util.PointString( q ) );
              }
              if( null == p0 )
              {
            p0 = p; // save loop start point
              }
            }
            Debug.Assert( q.IsAlmostEqualTo( p0 ),
              "expected last endpoint to equal loop start point" );

            jtloops.Add( jtloop );
              }
              return jtloops;
        }
        /// <summary>
        /// Return a closed loop of integer-based points
        /// scaled to millimetres from a given Revit model
        /// face in feet.
        /// </summary>
        internal static JtLoop GetLoop(
            Autodesk.Revit.Creation.Application creapp,
            Face face)
        {
            JtLoop loop = null;

              foreach( EdgeArray a in face.EdgeLoops )
              {
            int nEdges = a.Size;

            List<Curve> curves
              = new List<Curve>( nEdges );

            XYZ p0 = null; // loop start point
            XYZ p; // edge start point
            XYZ q = null; // edge end point

            // Test ValidateCurveLoops

            //CurveLoop loopIfc = new CurveLoop();

            foreach( Edge e in a )
            {
              // This requires post-processing using
              // SortCurvesContiguous:

              Curve curve = e.AsCurve();

              if( _debug_output )
              {
            p = curve.GetEndPoint( 0 );
            q = curve.GetEndPoint( 1 );
            Debug.Print( "{0} --> {1}",
              Util.PointString( p ),
              Util.PointString( q ) );
              }

              // This returns the curves already
              // correctly oriented:

              curve = e.AsCurveFollowingFace(
            face );

              if( _debug_output )
              {
            p = curve.GetEndPoint( 0 );
            q = curve.GetEndPoint( 1 );
            Debug.Print( "{0} --> {1} following face",
              Util.PointString( p ),
              Util.PointString( q ) );
              }

              curves.Add( curve );

              // Throws an exception saying "This curve
              // will make the loop not contiguous.
              // Parameter name: pCurve"

              //loopIfc.Append( curve );
            }

            // We never reach this point:

            //List<CurveLoop> loopsIfc
            //  = new List<CurveLoop>( 1 );

            //loopsIfc.Add( loopIfc );

            //IList<CurveLoop> loopsIfcOut = ExporterIFCUtils
            //  .ValidateCurveLoops( loopsIfc, XYZ.BasisZ );

            // This is no longer needed if we use
            // AsCurveFollowingFace instead of AsCurve:

            CurveUtils.SortCurvesContiguous(
              creapp, curves, _debug_output );

            q = null;

            loop = new JtLoop( nEdges );

            foreach( Curve curve in curves )
            {
              // Todo: handle non-linear curve.
              // Especially: if two long lines have a
              // short arc in between them, skip the arc
              // and extend both lines.

              p = curve.GetEndPoint( 0 );

              Debug.Assert( null == q
            || q.IsAlmostEqualTo( p, 1e-04 ),
            string.Format(
              "expected last endpoint to equal current start point, not distance {0}",
              ( null == q ? 0 : p.DistanceTo( q ) ) ) );

              q = curve.GetEndPoint( 1 );

              if( _debug_output )
              {
            Debug.Print( "{0} --> {1}",
              Util.PointString( p ),
              Util.PointString( q ) );
              }

              if( null == p0 )
              {
            p0 = p; // save loop start point
              }

              int n = -1;

              if( _tessellate_curves
            && _min_tessellation_curve_length_in_feet
              < q.DistanceTo( p ) )
              {
            IList<XYZ> pts = curve.Tessellate();
            n = pts.Count;

            Debug.Assert( 1 < n, "expected at least two points" );
            Debug.Assert( p.IsAlmostEqualTo( pts[0] ), "expected tessellation start equal curve start point" );
            Debug.Assert( q.IsAlmostEqualTo( pts[n - 1] ), "expected tessellation end equal curve end point" );

            if( 2 == n )
            {
              n = -1; // this is a straight line
            }
            else
            {
              --n; // skip last point

              for( int i = 0; i < n; ++i )
              {
                loop.Add( new Point2dInt( pts[i] ) );
              }
            }
              }

              // If tessellation is disabled,
              // or curve is too short to tessellate,
              // or has only two tessellation points,
              // just add the start point:

              if( -1 == n )
              {
            loop.Add( new Point2dInt( p ) );
              }
            }
            Debug.Assert( q.IsAlmostEqualTo( p0, 1e-05 ),
              string.Format(
            "expected last endpoint to equal current start point, not distance {0}",
            p0.DistanceTo( q ) ) );
              }
              return loop;
        }