From 468c72d13e6e8aaf56c5f9cd5b73f93bdabe7bda Mon Sep 17 00:00:00 2001 From: Jeremy Tammik Date: Mon, 3 Feb 2014 20:37:12 +0100 Subject: [PATCH] implemented CmdDeleteUnusedRefPlanes --- BcSamples.txt | 8 + .../BuildingCoder/BuildingCoder.csproj | 1 + .../BuildingCoder/CmdDeleteUnusedRefPlanes.cs | 195 ++++++++++++++++++ .../CmdDimensionWallsFindRefs.cs | 148 ++++++------- .../BuildingCoder/Properties/AssemblyInfo.cs | 4 +- 5 files changed, 280 insertions(+), 76 deletions(-) create mode 100644 BuildingCoder/BuildingCoder/CmdDeleteUnusedRefPlanes.cs diff --git a/BcSamples.txt b/BcSamples.txt index e46bd756..6c52df6b 100644 --- a/BcSamples.txt +++ b/BcSamples.txt @@ -913,3 +913,11 @@ LargeImage: Image: C:\a\lib\revit\2014\bc\BuildingCoder\BuildingCoder\bin\Debug\BuildingCoder.dll BuildingCoder.CmdRollingOffset # version 2014.0.106.0 + +ADN Bc N-Z +Delete Unused Reference Planes +Delete unnamed non-hosting reference planes +LargeImage: +Image: +C:\a\lib\revit\2014\bc\BuildingCoder\BuildingCoder\bin\Debug\BuildingCoder.dll +BuildingCoder.CmdDeleteUnusedRefPlanes # version 2014.0.107.0 diff --git a/BuildingCoder/BuildingCoder/BuildingCoder.csproj b/BuildingCoder/BuildingCoder/BuildingCoder.csproj index 6d3fafcc..2d414e9a 100644 --- a/BuildingCoder/BuildingCoder/BuildingCoder.csproj +++ b/BuildingCoder/BuildingCoder/BuildingCoder.csproj @@ -136,6 +136,7 @@ + diff --git a/BuildingCoder/BuildingCoder/CmdDeleteUnusedRefPlanes.cs b/BuildingCoder/BuildingCoder/CmdDeleteUnusedRefPlanes.cs new file mode 100644 index 00000000..1bc8c5b4 --- /dev/null +++ b/BuildingCoder/BuildingCoder/CmdDeleteUnusedRefPlanes.cs @@ -0,0 +1,195 @@ +#region Header +// +// CmdDeleteUnusedRefPlanes.cs - delete unnamed non-hosting reference planes +// +// Copyright (C) 2014 by Jeremy Tammik, Autodesk Inc. All rights reserved. +// +#endregion // Header + +#region Namespaces +using System; +using System.Collections.Generic; +using System.Linq; +using System.Diagnostics; +using Autodesk.Revit.ApplicationServices; +using Autodesk.Revit.Attributes; +using Autodesk.Revit.DB; +using Autodesk.Revit.DB.Plumbing; +using Autodesk.Revit.UI; +using Autodesk.Revit.UI.Selection; +#endregion // Namespaces + +namespace BuildingCoder +{ + /// + /// Delete all reference planes that have not been + /// named and are not hosting any elements. + /// In other words, check whether the reference + /// plane has been named. + /// If not, check whether it hosts any elements. + /// If not, delete it. + /// Actually, to check whether it hosts any + /// elements, we delete it temporarily anyway, as + /// described in + /// Object Relationships http://thebuildingcoder.typepad.com/blog/2010/03/object-relationships.html + /// Object Relationships in VB http://thebuildingcoder.typepad.com/blog/2010/03/object-relationships-in-vb.html + /// Temporary Transaction Trick Touchup http://thebuildingcoder.typepad.com/blog/2012/11/temporary-transaction-trick-touchup.html + /// The deletion returns the number of elements + /// deleted. If this number is greater than one (the + /// ref plane itself), it hosted something. In that + /// case, roll back the transaction and do not delete. + /// + [Transaction( TransactionMode.Manual )] + class CmdDeleteUnusedRefPlanes : IExternalCommand + { + static int _i = 0; + + /// + /// Delete the given reference plane + /// if it is not hosting anything. + /// + /// True if the given reference plane + /// was in fact deleted, else false. + bool DeleteIfNotHosting( ReferencePlane rp ) + { + bool rc = false; + + Document doc = rp.Document; + + using( Transaction tx = new Transaction( doc ) ) + { + tx.Start( "Delete ReferencePlane " + + ( ++_i ).ToString() ); + + // Deletion simply fails if the reference + // plane hosts anything. If so, the return + // value ids collection is null. + // In Revit 2014, in that case, the call + // throws an exception "ArgumentException: + // ElementId cannot be deleted." + + try + { + ICollection ids = doc.Delete( + rp.Id ); + + tx.Commit(); + rc = true; + } + catch( ArgumentException ) + { + tx.RollBack(); + } + } + 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; + + // Construct a parameter filter to get only + // unnamed reference planes, i.e. reference + // planes whose name equals the empty string: + + BuiltInParameter bip + = BuiltInParameter.DATUM_TEXT; + + ParameterValueProvider provider + = new ParameterValueProvider( + new ElementId( bip ) ); + + FilterStringRuleEvaluator evaluator + = new FilterStringEquals(); + + FilterStringRule rule = new FilterStringRule( + provider, evaluator, "", false ); + + ElementParameterFilter filter + = new ElementParameterFilter( rule ); + + FilteredElementCollector col + = new FilteredElementCollector( doc ) + .OfClass( typeof( ReferencePlane ) ) + .WherePasses( filter ); + + int n = 0; + int nDeleted = 0; + + // No need to cast ... this is pretty nifty, + // I find ... grab the elements as ReferencePlane + // instances, since the filter guarantees that + // only ReferencePlane instances are selected. + // In Revit 2014, this attempt to delete the + // reference planes while iterating over the + // filtered element collector throws an exception: + // Autodesk.Revit.Exceptions.InvalidOperationException: + // HResult=-2146233088 + // Message=The iterator cannot proceed due to + // changes made to the Element table in Revit's + // database (typically, This can be the result + // of an Element deletion). + // + //foreach( ReferencePlane rp in col ) + //{ + // ++n; + // nDeleted += DeleteIfNotHosting( rp ) ? 1 : 0; + //} + + ICollection ids = col.ToElementIds(); + + n = ids.Count(); + + if( 0 < n ) + { + using( Transaction tx = new Transaction( doc ) ) + { + tx.Start( string.Format( + "Delete {0} ReferencePlane{1}", + n, Util.PluralSuffix( n ) ) ); + + // This also causes the exception "One or more of + // the elementIds cannot be deleted. Parameter + // name: elementIds + // + //ICollection ids2 = doc.Delete( + // ids ); + //nDeleted = ids2.Count(); + + List ids2 = new List( + ids ); + + foreach( ElementId id in ids2 ) + { + try + { + ICollection ids3 = doc.Delete( + id ); + + nDeleted += ids3.Count; + } + catch( Autodesk.Revit.Exceptions.ArgumentException ) + { + } + } + + tx.Commit(); + } + } + + Util.InfoMsg( string.Format( + "{0} unnamed reference plane{1} examined, " + + "{2} element{3} in total were deleted.", + n, Util.PluralSuffix( n ), + nDeleted, Util.PluralSuffix( nDeleted ) ) ); + + return Result.Succeeded; + } + } +} diff --git a/BuildingCoder/BuildingCoder/CmdDimensionWallsFindRefs.cs b/BuildingCoder/BuildingCoder/CmdDimensionWallsFindRefs.cs index a5634d9e..f052ad9c 100644 --- a/BuildingCoder/BuildingCoder/CmdDimensionWallsFindRefs.cs +++ b/BuildingCoder/BuildingCoder/CmdDimensionWallsFindRefs.cs @@ -234,7 +234,7 @@ WallSelectionFilter f // Shoot a ray back from the second // picked wall towards first: - Debug.Print( + Debug.Print( "Shooting ray from {0} in direction {1}", Util.PointString( pts[1] ), Util.PointString( normal ) ); @@ -268,7 +268,7 @@ ReferenceIntersector ri = new ReferenceIntersector( walls[0].Id, FindReferenceTarget.Element, view ); - ReferenceWithContext ref2 + ReferenceWithContext ref2 = ri.FindNearest( pts[1], normal ); if( null == ref2 ) @@ -281,99 +281,99 @@ ReferenceWithContext ref2 #region Obsolete code to determine the closest reference #if NEED_TO_DETERMINE_CLOSEST_REFERENCE - // Store the references to the wall surfaces: + // Store the references to the wall surfaces: - Reference[] surfrefs = new Reference[2] { - null, null }; + Reference[] surfrefs = new Reference[2] { + null, null }; - // Find the two closest intersection - // points on each of the two walls: + // Find the two closest intersection + // points on each of the two walls: - double[] minDistance = new double[2] { - double.MaxValue, - double.MaxValue }; + 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 + //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 + Reference r = rc.GetReference(); + Element e = doc.GetElement( r ); // 2012 - if( e is Wall ) - { - i = ids.IndexOf( e.Id.IntegerValue ); + 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( -1 < i - && ElementReferenceType.REFERENCE_TYPE_SURFACE - == r.ElementReferenceType ) + 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] ) { - //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; - } - } + surfrefs[i] = r; + minDistance[i] = rc.Proximity; } } } + } + } - if( null == surfrefs[0] ) - { - message = "No suitable face intersection " - + "points found on first wall."; + if( null == surfrefs[0] ) + { + message = "No suitable face intersection " + + "points found on first wall."; - return Result.Failed; - } + return Result.Failed; + } - if( null == surfrefs[1] ) - { - message = "No suitable face intersection " - + "points found on second wall."; + if( null == surfrefs[1] ) + { + message = "No suitable face intersection " + + "points found on second wall."; - return Result.Failed; - } + return Result.Failed; + } - CmdDimensionWallsIterateFaces - .CreateDimensionElement( doc.ActiveView, - pts[0], surfrefs[0], pts[1], surfrefs[1] ); + 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) ); + pts[0], ref2.GetReference(), pts[1], refs.get_Item( 1 ) ); return Result.Succeeded; } diff --git a/BuildingCoder/BuildingCoder/Properties/AssemblyInfo.cs b/BuildingCoder/BuildingCoder/Properties/AssemblyInfo.cs index 041c2ca3..e7bd3fc2 100644 --- a/BuildingCoder/BuildingCoder/Properties/AssemblyInfo.cs +++ b/BuildingCoder/BuildingCoder/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion( "2014.0.106.8" )] -[assembly: AssemblyFileVersion( "2014.0.106.8" )] +[assembly: AssemblyVersion( "2014.0.107.0" )] +[assembly: AssemblyFileVersion( "2014.0.107.0" )]