/// <summary>
        /// External command mainline. Run in the sample
        /// model Z:\a\rvt\dimension_case_2015.rvt, e.g.
        /// </summary>
        public Result Execute(
            ExternalCommandData commandData,
            ref string message,
            ElementSet elements)
        {
            UIApplication app   = commandData.Application;
            UIDocument    uidoc = app.ActiveUIDocument;
            Document      doc   = uidoc.Document;

            JtPairPicker <FamilyInstance> picker
                = new JtPairPicker <FamilyInstance>(uidoc);

            Result rc = picker.Pick();

            if (Result.Failed == rc)
            {
                message = "We need at least two "
                          + "FamilyInstance elements in the model.";
            }
            else if (Result.Succeeded == rc)
            {
                IList <FamilyInstance> a = picker.Selected;

                _opt = new Options();
                _opt.ComputeReferences        = true;
                _opt.IncludeNonVisibleObjects = true;

                XYZ[]       pts  = new XYZ[2];
                Reference[] refs = new Reference[2];

                pts[0] = (a[0].Location as LocationPoint).Point;
                pts[1] = (a[1].Location as LocationPoint).Point;

                refs[0] = GetFamilyInstancePointReference(a[0]);
                refs[1] = GetFamilyInstancePointReference(a[1]);

                CmdDimensionWallsIterateFaces
                .CreateDimensionElement(doc.ActiveView,
                                        pts[0], refs[0], pts[1], refs[1]);
            }
            return(rc);
        }
        public Result Execute(
            ExternalCommandData commandData,
            ref string message,
            ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument    uidoc = uiapp.ActiveUIDocument;
            Application   app   = uiapp.Application;
            Document      doc   = uidoc.Document;

            //// Select all pipes in the entire model.

            //List<Pipe> pipes = new List<Pipe>(
            //  new FilteredElementCollector( doc )
            //    .OfClass( typeof( Pipe ) )
            //    .ToElements()
            //    .Cast<Pipe>() );

            //int n = pipes.Count;

            //// If there are less than two,
            //// there is nothing we can do.

            //if( 2 > n )
            //{
            //  message = _prompt;
            //  return Result.Failed;
            //}

            //// If there are exactly two, pick those.

            //if( 2 < n )
            //{
            //  // Else, check for a pre-selection.

            //  pipes.Clear();

            //  Selection sel = uidoc.Selection;

            //  //n = sel.Elements.Size; // 2014

            //  ICollection<ElementId> ids
            //    = sel.GetElementIds(); // 2015

            //  n = ids.Count; // 2015

            //  Debug.Print( "{0} pre-selected elements.",
            //    n );

            //  // If two or more model pipes were pre-
            //  // selected, use the first two encountered.

            //  if( 1 < n )
            //  {
            //    //foreach( Element e in sel.Elements ) // 2014

            //    foreach( ElementId id in ids ) // 2015
            //    {
            //      Pipe c = doc.GetElement( id ) as Pipe;

            //      if( null != c )
            //      {
            //        pipes.Add( c );

            //        if( 2 == pipes.Count )
            //        {
            //          Debug.Print( "Found two model pipes, "
            //            + "ignoring everything else." );

            //          break;
            //        }
            //      }
            //    }
            //  }

            //  // Else, prompt for an
            //  // interactive post-selection.

            //  if( 2 != pipes.Count )
            //  {
            //    pipes.Clear();

            //    try
            //    {
            //      Reference r = sel.PickObject(
            //        ObjectType.Element,
            //        new PipeElementSelectionFilter(),
            //        "Please pick first pipe." );

            //      pipes.Add( doc.GetElement( r.ElementId )
            //        as Pipe );
            //    }
            //    catch( Autodesk.Revit.Exceptions
            //      .OperationCanceledException )
            //    {
            //      return Result.Cancelled;
            //    }

            //    try
            //    {
            //      Reference r = sel.PickObject(
            //        ObjectType.Element,
            //        new PipeElementSelectionFilter(),
            //        "Please pick second pipe." );

            //      pipes.Add( doc.GetElement( r.ElementId )
            //        as Pipe );
            //    }
            //    catch( Autodesk.Revit.Exceptions
            //      .OperationCanceledException )
            //    {
            //      return Result.Cancelled;
            //    }
            //  }
            //}

            JtPairPicker <Pipe> picker
                = new JtPairPicker <Pipe>(uidoc);

            Result rc = picker.Pick();

            if (Result.Failed == rc)
            {
                message = _prompt;
            }

            if (Result.Succeeded != rc)
            {
                return(rc);
            }

            IList <Pipe> pipes = picker.Selected;

            // Check for same pipe system type.

            ElementId systemTypeId
                = pipes[0].MEPSystem.GetTypeId();

            Debug.Assert(pipes[1].MEPSystem.GetTypeId()
                         .IntegerValue.Equals(
                             systemTypeId.IntegerValue),
                         "expected two similar pipes");

            // Check for same pipe level.

            ElementId levelId = pipes[0].LevelId;

            Debug.Assert(
                pipes[1].LevelId.IntegerValue.Equals(
                    levelId.IntegerValue),
                "expected two pipes on same level");

            // Extract data from the two selected pipes.

            double wall_thickness = GetWallThickness(pipes[0]);

            Debug.Print("{0} has wall thickness {1}",
                        Util.ElementDescription(pipes[0]),
                        Util.RealString(wall_thickness));

            Curve c0 = pipes[0].GetCurve();
            Curve c1 = pipes[1].GetCurve();

            if (!(c0 is Line) || !(c1 is Line))
            {
                message = _prompt
                          + " Expected straight pipes.";

                return(Result.Failed);
            }

            XYZ p00 = c0.GetEndPoint(0);
            XYZ p01 = c0.GetEndPoint(1);

            XYZ p10 = c1.GetEndPoint(0);
            XYZ p11 = c1.GetEndPoint(1);

            XYZ v0 = p01 - p00;
            XYZ v1 = p11 - p10;

            if (!Util.IsParallel(v0, v1))
            {
                message = _prompt
                          + " Expected parallel pipes.";

                return(Result.Failed);
            }

            // Select the two pipe endpoints
            // that are farthest apart.

            XYZ p0 = p00.DistanceTo(p10) > p01.DistanceTo(p10)
        ? p00
        : p01;

            XYZ p1 = p10.DistanceTo(p0) > p11.DistanceTo(p0)
        ? p10
        : p11;

            XYZ pm = 0.5 * (p0 + p1);

            XYZ v = p1 - p0;

            if (Util.IsParallel(v, v0))
            {
                message = "The selected pipes are colinear.";
                return(Result.Failed);
            }

            // Normal vector of the plane defined by the
            // two parallel and offset pipes, which is
            // the plane hosting the rolling offset

            XYZ z = v.CrossProduct(v1);

            // Vector perpendicular to v0 and v0 and
            // z, i.e. vector pointing from the first pipe
            // to the second in the cross sectional view.

            XYZ w = z.CrossProduct(v1).Normalize();

            // Offset distance perpendicular to pipe direction

            double distanceAcross = Math.Abs(
                v.DotProduct(w));

            // Distance between endpoints parallel
            // to pipe direction

            double distanceAlong = Math.Abs(
                v.DotProduct(v1.Normalize()));

            Debug.Assert(Util.IsEqual(v.GetLength(),
                                      Math.Sqrt(distanceAcross * distanceAcross
                                                + distanceAlong * distanceAlong)),
                         "expected Pythagorean equality here");

            // The required offset pipe angle.

            double angle = 45 * Math.PI / 180.0;

            // The angle on the other side.

            double angle2 = 0.5 * Math.PI - angle;

            double length = distanceAcross * Math.Tan(angle2);

            double halfLength = 0.5 * length;

            // How long should the pipe stubs become?

            double remainingPipeLength
                = 0.5 * (distanceAlong - length);

            if (0 > v1.DotProduct(v))
            {
                v1.Negate();
            }

            v1 = v1.Normalize();

            XYZ q0 = p0 + remainingPipeLength * v1;

            XYZ q1 = p1 - remainingPipeLength * v1;

            using (Transaction tx = new Transaction(doc))
            {
                // Determine pipe diameter for creating
                // matching pipes and fittings

                Pipe pipe = pipes[0];

                double diameter = pipe
                                  .get_Parameter(bipDiameter) // "Diameter"
                                  .AsDouble();

                // Pipe type for calls to doc.Create.NewPipe

                PipeType pipe_type_standard
                    = new FilteredElementCollector(doc)
                      .OfClass(typeof(PipeType))
                      .Cast <PipeType>()
                      .Where <PipeType>(e
                                        => e.Name.Equals("Standard"))
                      .FirstOrDefault <PipeType>();

                Debug.Assert(
                    pipe_type_standard.Id.IntegerValue.Equals(
                        pipe.PipeType.Id.IntegerValue),
                    "expected all pipes in this simple "
                    + "model to use the same pipe type");

                tx.Start("Rolling Offset");

                if (_place_model_line)
                {
                    // Trim or extend existing pipes

                    (pipes[0].Location as LocationCurve).Curve
                        = Line.CreateBound(p0, q0);

                    (pipes[1].Location as LocationCurve).Curve
                        = Line.CreateBound(p1, q1);

                    // Add a model line for the rolling offset pipe

                    Creator creator = new Creator(doc);

                    Line line = Line.CreateBound(q0, q1);

                    creator.CreateModelCurve(line);

                    pipe = null;
                }
                else if (_place_fittings)
                {
                    // Set active work plane to the rolling
                    // offset plane... removed again, since
                    // this has no effect at all on the
                    // fitting placement or rotation.
                    //
                    //Plane plane = new Plane( z, q0 );
                    //
                    //SketchPlane sp = SketchPlane.Create(
                    //  doc, plane );
                    //
                    //uidoc.ActiveView.SketchPlane = sp;
                    //uidoc.ActiveView.ShowActiveWorkPlane();

                    FamilySymbol symbol
                        = new FilteredElementCollector(doc)
                          .OfClass(typeof(FamilySymbol))
                          .OfCategory(BuiltInCategory.OST_PipeFitting)
                          .Cast <FamilySymbol>()
                          .Where <FamilySymbol>(e
                                                => e.Family.Name.Contains("Elbow - Generic"))
                          .FirstOrDefault <FamilySymbol>();

                    // Set up first 45 degree elbow fitting

                    FamilyInstance fitting0 = doc.Create
                                              .NewFamilyInstance(q0, symbol,
                                                                 StructuralType.NonStructural);

                    fitting0.LookupParameter("Angle").Set(
                        45.0 * Math.PI / 180.0);

                    //fitting0.get_Parameter( bipDiameter ) // does not exist
                    //  .Set( diameter );

                    fitting0.LookupParameter("Nominal Radius")
                    .Set(0.5 * diameter);

                    Line axis = Line.CreateBound(p0, q0);
                    angle = z.AngleTo(XYZ.BasisZ);

                    ElementTransformUtils.RotateElement(
                        doc, fitting0.Id, axis, Math.PI - angle);

                    Connector con0 = Util.GetConnectorClosestTo(
                        fitting0, p0);

                    // Trim or extend existing pipe

                    (pipes[0].Location as LocationCurve).Curve
                        = Line.CreateBound(p0, con0.Origin);

                    // Connect pipe to fitting

                    Util.Connect(con0.Origin, pipe, fitting0);

                    // Set up second 45 degree elbow fitting

                    FamilyInstance fitting1 = doc.Create
                                              .NewFamilyInstance(q1, symbol,
                                                                 StructuralType.NonStructural);

                    //fitting1.get_Parameter( "Angle" ).Set( 45.0 * Math.PI / 180.0 ); // 2014
                    //fitting1.get_Parameter( "Nominal Radius" ).Set( 0.5 * diameter ); // 2014

                    fitting1.LookupParameter("Angle").Set(45.0 * Math.PI / 180.0);  // 2015
                    fitting1.LookupParameter("Nominal Radius").Set(0.5 * diameter); // 2015

                    axis = Line.CreateBound(
                        q1, q1 + XYZ.BasisZ);

                    ElementTransformUtils.RotateElement(
                        doc, fitting1.Id, axis, Math.PI);

                    axis = Line.CreateBound(q1, p1);

                    ElementTransformUtils.RotateElement(
                        doc, fitting1.Id, axis, Math.PI - angle);

                    Connector con1 = Util.GetConnectorClosestTo(
                        fitting1, p1);

                    (pipes[1].Location as LocationCurve).Curve
                        = Line.CreateBound(con1.Origin, p1);

                    Util.Connect(con1.Origin, fitting1, pipes[1]);

                    con0 = Util.GetConnectorClosestTo(
                        fitting0, pm);

                    con1 = Util.GetConnectorClosestTo(
                        fitting1, pm);

                    // Connecting one fitting to the other does
                    // not insert a pipe in between. If the
                    // system is edited later, however, the two
                    // fittings snap together.
                    //
                    //con0.ConnectTo( con1 );

                    // Create rolling offset pipe segment

                    //pipe = doc.Create.NewPipe( con0.Origin, // 2014
                    //  con1.Origin, pipe_type_standard );

                    pipe = Pipe.Create(doc,
                                       pipe_type_standard.Id, levelId, con0, con1); // 2015

                    pipe.get_Parameter(bipDiameter)
                    .Set(diameter);

                    // Connect rolling offset pipe segment
                    // with elbow fittings at each end

                    Util.Connect(con0.Origin, fitting0, pipe);
                    Util.Connect(con1.Origin, pipe, fitting1);
                }
                else
                {
                    if (_use_static_pipe_create)
                    {
                        // Element id arguments to Pipe.Create.

                        ElementId idSystem;
                        ElementId idType;
                        ElementId idLevel;

                        // All these values are invalid for idSystem:

                        ElementId idSystem1 = pipe.MEPSystem.Id;
                        ElementId idSystem2 = ElementId.InvalidElementId;
                        ElementId idSystem3 = PipingSystem.Create(
                            doc, pipe.MEPSystem.GetTypeId(), "Tbc")
                                              .Id;

                        // This throws an argument exception saying
                        // The systemTypeId is not valid piping system type.
                        // Parameter name: systemTypeId

                        //pipe = Pipe.Create( doc, idSystem,
                        //  idType, idLevel, q0, q1 );

                        // Retrieve pipe system type, e.g.
                        // hydronic supply.

                        PipingSystemType pipingSystemType
                            = new FilteredElementCollector(doc)
                              .OfClass(typeof(PipingSystemType))
                              .OfType <PipingSystemType>()
                              .FirstOrDefault(st
                                              => st.SystemClassification
                                              == MEPSystemClassification
                                              .SupplyHydronic);

                        if (null == pipingSystemType)
                        {
                            message = "Could not find hydronic supply piping system type";
                            return(Result.Failed);
                        }

                        idSystem = pipingSystemType.Id;

                        Debug.Assert(pipe.get_Parameter(
                                         BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM)
                                     .AsElementId().IntegerValue.Equals(
                                         idSystem.IntegerValue),
                                     "expected same piping system element id");

                        // Retrieve the PipeType.

                        PipeType pipeType =
                            new FilteredElementCollector(doc)
                            .OfClass(typeof(PipeType))
                            .OfType <PipeType>()
                            .FirstOrDefault();

                        if (null == pipeType)
                        {
                            message = "Could not find pipe type";
                            return(Result.Failed);
                        }

                        idType = pipeType.Id;

                        Debug.Assert(pipe.get_Parameter(
                                         BuiltInParameter.ELEM_TYPE_PARAM)
                                     .AsElementId().IntegerValue.Equals(
                                         idType.IntegerValue),
                                     "expected same pipe type element id");

                        Debug.Assert(pipe.PipeType.Id.IntegerValue
                                     .Equals(idType.IntegerValue),
                                     "expected same pipe type element id");

                        // Retrieve the reference level.
                        // pipe.LevelId is not the correct source!

                        idLevel = pipe.get_Parameter(
                            BuiltInParameter.RBS_START_LEVEL_PARAM)
                                  .AsElementId();

                        // Create the rolling offset pipe.

                        pipe = Pipe.Create(doc,
                                           idSystem, idType, idLevel, q0, q1);
                    }
                    else
                    {
                        //pipe = doc.Create.NewPipe( q0, q1, pipe_type_standard ); // 2014

                        pipe = Pipe.Create(doc, systemTypeId,
                                           pipe_type_standard.Id, levelId, q0, q1); // 2015
                    }

                    pipe.get_Parameter(bipDiameter)
                    .Set(diameter);

                    // Connect rolling offset pipe segment
                    // directly with the neighbouring original
                    // pipes
                    //
                    //Util.Connect( q0, pipes[0], pipe );
                    //Util.Connect( q1, pipe, pipes[1] );

                    // NewElbowFitting performs the following:
                    // - select appropriate fitting family and type
                    // - place and orient a family instance
                    // - set its parameters appropriately
                    // - connect it with its neighbours

                    Connector con0 = Util.GetConnectorClosestTo(
                        pipes[0], q0);

                    Connector con = Util.GetConnectorClosestTo(
                        pipe, q0);

                    doc.Create.NewElbowFitting(con0, con);

                    Connector con1 = Util.GetConnectorClosestTo(
                        pipes[1], q1);

                    con = Util.GetConnectorClosestTo(
                        pipe, q1);

                    doc.Create.NewElbowFitting(con, con1);
                }

                tx.Commit();
            }
            return(Result.Succeeded);
        }