/// <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;
        }