Beispiel #1
0
        //public void CreateModelLine( XYZ p, XYZ q )
        //{
        //  if( p.IsAlmostEqualTo( q ) )
        //  {
        //    throw new ArgumentException(
        //      "Expected two different points." );
        //  }
        //  Line line = Line.CreateBound( p, q );
        //  if( null == line )
        //  {
        //    throw new Exception(
        //      "Geometry line creation failed." );
        //  }
        //  _credoc.NewModelCurve( line,
        //    NewSketchPlanePassLine( line ) );
        //}

        /// <summary>
        /// Return a new sketch plane containing the given curve.
        /// Update, later: please note that the Revit API provides
        /// an overload of the NewPlane method taking a CurveArray
        /// argument, which could presumably be used instead.
        /// </summary>
        SketchPlane NewSketchPlaneContainCurve(
            Curve curve)
        {
            XYZ p      = curve.GetEndPoint(0);
            XYZ normal = GetCurveNormal(curve);

            //Plane plane = _creapp.NewPlane( normal, p ); // 2016
            Plane plane = Plane.CreateByNormalAndOrigin(normal, p); // 2017

#if DEBUG
            if (!(curve is Line))
            {
                //CurveArray a = _creapp.NewCurveArray();
                //a.Append( curve );
                //Plane plane2 = _creapp.NewPlane( a ); // 2016

                List <Curve> a = new List <Curve>(1);
                a.Add(curve);
                CurveLoop b      = CurveLoop.Create(a);
                Plane     plane2 = b.GetPlane(); // 2017


                Debug.Assert(Util.IsParallel(plane2.Normal,
                                             plane.Normal), "expected equal planes");

                Debug.Assert(Util.IsZero(plane2.SignedDistanceTo(
                                             plane.Origin)), "expected equal planes");
            }
#endif // DEBUG

            //return _credoc.NewSketchPlane( plane ); // 2013

            return(SketchPlane.Create(_doc, plane)); // 2014
        }
Beispiel #2
0
        /// <summary>
        /// Return the closest planar face to a given point
        /// p on the element e with a given normal vector.
        /// </summary>
        static Face GetClosestFace(
            Element e,
            XYZ p,
            XYZ normal,
            Options opt)
        {
            Face            face         = null;
            double          min_distance = double.MaxValue;
            GeometryElement geo          = e.get_Geometry(opt);

            //GeometryObjectArray objects = geo.Objects; // 2012
            //foreach( GeometryObject obj in objects ) // 2012

            foreach (GeometryObject obj in geo) // 2013
            {
                Solid solid = obj as Solid;
                if (solid != null)
                {
                    FaceArray fa = solid.Faces;
                    foreach (Face f in fa)
                    {
                        PlanarFace pf = f as PlanarFace;

                        Debug.Assert(null != pf,
                                     "expected planar wall faces");

                        if (null != pf
                            //&& normal.IsAlmostEqualTo( pf.Normal )
                            && Util.IsParallel(normal, pf.FaceNormal))
                        {
                            //XYZ q = pf.Project( p ).XYZPoint; // Project returned null once
                            //double d = q.DistanceTo( p );

                            XYZ    v = p - pf.Origin;
                            double d = v.DotProduct(-pf.FaceNormal);
                            if (d < min_distance)
                            {
                                face         = f;
                                min_distance = d;
                            }
                        }
                    }
                }
            }
            return(face);
        }
Beispiel #3
0
        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;

            // obtain the current selection and pick
            // out all walls from it:

            //Selection sel = uidoc.Selection; // 2014

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

            List <Wall> walls = new List <Wall>(2);

            //foreach( Element e in sel.Elements ) // 2014

            foreach (ElementId id in ids) // 2015
            {
                Element e = doc.GetElement(id);

                if (e is Wall)
                {
                    walls.Add(e as Wall);
                }
            }

            if (2 != walls.Count)
            {
                message = _prompt;
                return(Result.Failed);
            }

            // ensure the two selected walls are straight and
            // parallel; determine their mutual normal vector
            // and a point on each wall for distance
            // calculations:

            List <Line> lines     = new List <Line>(2);
            List <XYZ>  midpoints = new List <XYZ>(2);
            XYZ         normal    = null;

            foreach (Wall wall in walls)
            {
                LocationCurve lc    = wall.Location as LocationCurve;
                Curve         curve = lc.Curve;

                if (!(curve is Line))
                {
                    message = _prompt;
                    return(Result.Failed);
                }

                Line l = curve as Line;
                lines.Add(l);
                midpoints.Add(Util.Midpoint(l));

                if (null == normal)
                {
                    normal = Util.Normal(l);
                }
                else
                {
                    if (!Util.IsParallel(normal, Util.Normal(l)))
                    {
                        message = _prompt;
                        return(Result.Failed);
                    }
                }
            }

            // find the two closest facing faces on the walls;
            // they are vertical faces that are parallel to the
            // wall curve and closest to the other wall.

            Options opt = app.Create.NewGeometryOptions();

            opt.ComputeReferences = true;

            List <Face> faces = new List <Face>(2);

            faces.Add(GetClosestFace(walls[0], midpoints[1], normal, opt));
            faces.Add(GetClosestFace(walls[1], midpoints[0], normal, opt));

            // create the dimensioning:

            CreateDimensionElement(doc.ActiveView,
                                   midpoints[0], faces[0].Reference,
                                   midpoints[1], faces[1].Reference);

            return(Result.Succeeded);
        }
        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);
        }
Beispiel #5
0
        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 two walls and the dimension line point:

            Selection      sel  = uidoc.Selection;
            ReferenceArray refs = new ReferenceArray();

            try
            {
                ISelectionFilter f
                    = new JtElementsOfClassSelectionFilter <Wall>();

                refs.Append(sel.PickObject(
                                ObjectType.Element, f,
                                "Please select first wall"));

                refs.Append(sel.PickObject(
                                ObjectType.Element, f,
                                "Please pick dimension line "
                                + "point on second wall"));

                //rFace = sel.PickObject( ObjectType.Face,
                //  "Please select face on second wall at dimension line point" );
                //
                //rPoint = sel.PickObject( ObjectType.PointOnElement,
                //  "Please select point on first wall" );
            }
            catch (Autodesk.Revit.Exceptions.OperationCanceledException)
            {
                message = "No two walls selected";
                return(Result.Failed);
            }

            // Ensure the two selected walls are straight and
            // parallel; determine their mutual normal vector
            // and a point on each wall for distance
            // calculations:

            Wall[]     walls = new Wall[2];
            List <int> ids   = new List <int>(2);

            XYZ[]              pts   = new XYZ[2];
            Line[]             lines = new Line[2];
            IntersectionResult ir;
            XYZ normal = null;
            int i      = 0;

            foreach (Reference r in refs)
            {
                // 'Autodesk.Revit.DB.Reference.Element' is
                // obsolete: Property will be removed. Use
                // Document.GetElement(Reference) instead.
                //Wall wall = r.Element as Wall; // 2011

                Wall wall = doc.GetElement(r) as Wall; // 2012

                walls[i] = wall;
                ids.Add(wall.Id.IntegerValue);

                // Obtain location curve and
                // check that it is straight:

                LocationCurve lc = wall.Location
                                   as LocationCurve;

                Curve curve = lc.Curve;
                lines[i] = curve as Line;

                if (null == lines[i])
                {
                    message = _prompt;
                    return(Result.Failed);
                }

                // Obtain normal vectors
                // and ensure that they are equal,
                // i.e. walls are parallel:

                if (null == normal)
                {
                    normal = Util.Normal(lines[i]);
                }
                else
                {
                    if (!Util.IsParallel(normal,
                                         Util.Normal(lines[i])))
                    {
                        message = _prompt;
                        return(Result.Failed);
                    }
                }

                // Obtain pick points and project
                // onto wall location lines:

                XYZ p = r.GlobalPoint;
                ir = lines[i].Project(p);

                if (null == ir)
                {
                    message = string.Format(
                        "Unable to project pick point {0} "
                        + "onto wall location line.",
                        i);

                    return(Result.Failed);
                }

                pts[i] = ir.XYZPoint;

                Debug.Print(
                    "Wall {0} id {1} at {2}, {3} --> point {4}",
                    i, wall.Id.IntegerValue,
                    Util.PointString(lines[i].GetEndPoint(0)),
                    Util.PointString(lines[i].GetEndPoint(1)),
                    Util.PointString(pts[i]));

                if (0 < i)
                {
                    // Project dimension point selected on second wall
                    // back onto first wall, and ensure that normal
                    // points from second wall to first:

                    ir = lines[0].Project(pts[1]);
                    if (null == ir)
                    {
                        message = string.Format(
                            "Unable to project selected dimension "
                            + "line point {0} on second wall onto "
                            + "first wall's location line.",
                            Util.PointString(pts[1]));

                        return(Result.Failed);
                    }
                    pts[0] = ir.XYZPoint;
                }

                ++i;
            }

            XYZ v = pts[0] - pts[1];

            if (0 > v.DotProduct(normal))
            {
                normal = -normal;
            }

            // Shoot a ray back from the second
            // picked wall towards first:

            Debug.Print(
                "Shooting ray from {0} in direction {1}",
                Util.PointString(pts[1]),
                Util.PointString(normal));

            View3D view = Get3DView(doc);

            if (null == view)
            {
                message = "No 3D view named '{3D}' found; "
                          + "run the View > 3D View command once "
                          + "to generate it.";

                return(Result.Failed);
            }

            //refs = doc.FindReferencesByDirection(
            //  pts[1], normal, view ); // 2011

            //IList<ReferenceWithContext> refs2
            //  = doc.FindReferencesWithContextByDirection(
            //    pts[1], normal, view ); // 2012

            // In the Revit 2014 API, the call to
            // FindReferencesWithContextByDirection causes a
            // warning saying:
            // "FindReferencesWithContextByDirection is obsolete:
            // This method is deprecated in Revit 2014.
            // Use the ReferenceIntersector class instead."

            ReferenceIntersector ri
                = new ReferenceIntersector(
                      walls[0].Id, FindReferenceTarget.Element, view);

            ReferenceWithContext ref2
                = ri.FindNearest(pts[1], normal);

            if (null == ref2)
            {
                message = "ReferenceIntersector.FindNearest"
                          + " returned null!";

                return(Result.Failed);
            }

            #region Obsolete code to determine the closest reference
#if NEED_TO_DETERMINE_CLOSEST_REFERENCE
            // Store the references to the wall surfaces:

            Reference[] surfrefs = new Reference[2] {
                null, null
            };

            // Find the two closest intersection
            // points on each of the two walls:

            double[] minDistance = new double[2] {
                double.MaxValue,
                double.MaxValue
            };

            //foreach( Reference r in refs )
            foreach (ReferenceWithContext rc in refs2)
            {
                // 'Autodesk.Revit.DB.Reference.Element' is
                // obsolete: Property will be removed. Use
                // Document.GetElement(Reference) instead.
                //Element e = r.Element; // 2011

                Reference r = rc.GetReference();
                Element   e = doc.GetElement(r); // 2012

                if (e is Wall)
                {
                    i = ids.IndexOf(e.Id.IntegerValue);

                    if (-1 < i &&
                        ElementReferenceType.REFERENCE_TYPE_SURFACE
                        == r.ElementReferenceType)
                    {
                        //GeometryObject g = r.GeometryObject; // 2011
                        GeometryObject g = e.GetGeometryObjectFromReference(r); // 2012

                        if (g is PlanarFace)
                        {
                            PlanarFace face = g as PlanarFace;

                            Line line = (e.Location as LocationCurve)
                                        .Curve as Line;

                            Debug.Print(
                                "Wall {0} at {1}, {2} surface {3} "
                                + "normal {4} proximity {5}",
                                e.Id.IntegerValue,
                                Util.PointString(line.GetEndPoint(0)),
                                Util.PointString(line.GetEndPoint(1)),
                                Util.PointString(face.Origin),
                                Util.PointString(face.Normal),
                                rc.Proximity);

                            // First reference: assert it is a face on this wall
                            // and the distance is half the wall thickness.
                            // Second reference: the first reference on the other
                            // wall; assert the distance between the two references
                            // equals the distance between the wall location lines
                            // minus half of the sum of the two wall thicknesses.

                            if (rc.Proximity < minDistance[i])
                            {
                                surfrefs[i]    = r;
                                minDistance[i] = rc.Proximity;
                            }
                        }
                    }
                }
            }

            if (null == surfrefs[0])
            {
                message = "No suitable face intersection "
                          + "points found on first wall.";

                return(Result.Failed);
            }

            if (null == surfrefs[1])
            {
                message = "No suitable face intersection "
                          + "points found on second wall.";

                return(Result.Failed);
            }

            CmdDimensionWallsIterateFaces
            .CreateDimensionElement(doc.ActiveView,
                                    pts[0], surfrefs[0], pts[1], surfrefs[1]);
#endif // NEED_TO_DETERMINE_CLOSEST_REFERENCE
            #endregion // Obsolete code to determine the closest reference

            CmdDimensionWallsIterateFaces
            .CreateDimensionElement(doc.ActiveView,
                                    pts[0], ref2.GetReference(), pts[1], refs.get_Item(1));

            return(Result.Succeeded);
        }