/// <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.Curve.GetEndPoint(0);

                    jtloop.Add(new Point2dInt(p));

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

                    q = seg.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
                    }
                }
                Debug.Assert(q.IsAlmostEqualTo(p0),
                             "expected last endpoint to equal loop start point");

                jtloops.Add(jtloop);
            }
            return(jtloops);
        }
        /// <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>
        /// 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);
        }