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; }
///// <summary> ///// Allow selection of curve elements only. ///// </summary> //class CurveElementSelectionFilter : ISelectionFilter //{ // public bool AllowElement( Element e ) // { // return e is CurveElement; // } // public bool AllowReference( Reference r, XYZ p ) // { // return true; // } //} 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 model curves in the entire model. List<CurveElement> curves = new List<CurveElement>( new FilteredElementCollector( doc ) .OfClass( typeof( CurveElement ) ) .ToElements() .Cast<CurveElement>() ); int n = curves.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. curves.Clear(); Selection sel = uidoc.Selection; ICollection<ElementId> ids = sel.GetElementIds(); n = ids.Count; Debug.Print( "{0} pre-selected elements.", n ); // If two or more model curves were pre- // selected, use the first two encountered. if( 1 < n ) { foreach( ElementId id in ids ) { CurveElement c = doc.GetElement( id ) as CurveElement; if( null != c ) { curves.Add( c ); if( 2 == curves.Count ) { Debug.Print( "Found two model curves, " + "ignoring everything else." ); break; } } } } // Else, prompt for an // interactive post-selection. if( 2 != curves.Count ) { curves.Clear(); ISelectionFilter f = new JtElementsOfClassSelectionFilter<CurveElement>(); try { Reference r = sel.PickObject( ObjectType.Element, f, "Please pick first model curve." ); curves.Add( doc.GetElement( r.ElementId ) as CurveElement ); } catch( Autodesk.Revit.Exceptions .OperationCanceledException ) { return Result.Cancelled; } try { Reference r = sel.PickObject( ObjectType.Element, f, "Please pick second model curve." ); curves.Add( doc.GetElement( r.ElementId ) as CurveElement ); } catch( Autodesk.Revit.Exceptions .OperationCanceledException ) { return Result.Cancelled; } } } // Extract data from the two selected curves. Curve c0 = curves[0].GeometryCurve; Curve c1 = curves[1].GeometryCurve; double sp0 = c0.GetEndParameter( 0 ); double ep0 = c0.GetEndParameter( 1 ); double step0 = ( ep0 - sp0 ) / _nSegments; double sp1 = c1.GetEndParameter( 0 ); double ep1 = c1.GetEndParameter( 1 ); double step1 = ( ep1 - sp1 ) / _nSegments; Debug.Print( "Two curves' step size [start, end]:" + " {0} [{1},{2}] -- {3} [{4},{5}]", Util.RealString( step0 ), Util.RealString( sp0 ), Util.RealString( ep0 ), Util.RealString( step1 ), Util.RealString( sp1 ), Util.RealString( ep1 ) ); // Modify document within a transaction. using( Transaction tx = new Transaction( doc ) ) { Creator creator = new Creator( doc ); tx.Start( "MidCurve" ); // Current segment start points. double t0 = sp0; double t1 = sp1; XYZ p0 = c0.GetEndPoint( 0 ); XYZ p1 = c1.GetEndPoint( 0 ); XYZ p = Util.Midpoint( p0, p1 ); Debug.Assert( p0.IsAlmostEqualTo( c0.Evaluate( t0, false ) ), "expected equal start points" ); Debug.Assert( p1.IsAlmostEqualTo( c1.Evaluate( t1, false ) ), "expected equal start points" ); // Current segment end points. t0 += step0; t1 += step1; XYZ q0, q1, q; Line line; for( int i = 0; i < _nSegments; ++i, t0 += step0, t1 += step1 ) { q0 = c0.Evaluate( t0, false ); q1 = c1.Evaluate( t1, false ); q = Util.Midpoint( q0, q1 ); Debug.Print( "{0} {1} {2} {3}-{4} {5}-{6} {7}-{8}", i, Util.RealString( t0 ), Util.RealString( t1 ), Util.PointString( p0 ), Util.PointString( q0 ), Util.PointString( p1 ), Util.PointString( q1 ), Util.PointString( p ), Util.PointString( q ) ); // Create approximating curve segment. line = Line.CreateBound( p, q ); creator.CreateModelCurve( line ); p0 = q0; p1 = q1; p = q; } tx.Commit(); } return Result.Succeeded; }