├── AlignTag.v11.suo ├── AlignTag ├── Resources │ ├── alignTags.gif │ ├── AlignAppIcon.png │ ├── align-top-large.png │ ├── align-top-small.png │ ├── arrange-large.png │ ├── arrange-small.png │ ├── align-icon-large.png │ ├── align-icon-small.png │ ├── align-left-large.png │ ├── align-left-small.png │ ├── align-right-large.png │ ├── align-right-small.png │ ├── AlignBottomSmall (1).png │ ├── AlignLeftLarge (1).png │ ├── align-bottom-large.png │ ├── align-bottom-small.png │ ├── align-center-large.png │ ├── align-center-small.png │ ├── align-middle-large.png │ ├── align-middle-small.png │ ├── untangle-vertically-large.png │ ├── untangle-vertically-small.png │ ├── align-icon-grey-background.png │ ├── distribute-vertically-large.png │ ├── distribute-vertically-small.png │ ├── untangle-horizontally-large.png │ ├── untangle-horizontally-small.png │ ├── DistributeVerticallyLarge (1).png │ ├── distribute-horizontally-large.png │ └── distribute-horizontally-small.png ├── CopyToRevitAddinFolder.bat ├── AlignTag.addin ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ └── Resources.Designer (1).cs ├── PostBuild.ps1 ├── AlignExecute.cs ├── App.cs ├── Tools.cs ├── AlignTag.csproj ├── AnnotationElement.cs ├── Align.cs └── Arrange.cs ├── LICENSE ├── Align.Test ├── Align - Backup.Test.csproj ├── Align.Test.csproj └── AlignTest.cs ├── README.md ├── .gitignore └── AlignTag.sln /AlignTag.v11.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag.v11.suo -------------------------------------------------------------------------------- /AlignTag/Resources/alignTags.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/alignTags.gif -------------------------------------------------------------------------------- /AlignTag/Resources/AlignAppIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/AlignAppIcon.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-top-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-top-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-top-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-top-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/arrange-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/arrange-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/arrange-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/arrange-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-icon-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-icon-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-icon-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-left-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-left-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-left-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-left-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-right-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-right-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-right-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-right-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/AlignBottomSmall (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/AlignBottomSmall (1).png -------------------------------------------------------------------------------- /AlignTag/Resources/AlignLeftLarge (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/AlignLeftLarge (1).png -------------------------------------------------------------------------------- /AlignTag/Resources/align-bottom-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-bottom-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-bottom-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-bottom-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-center-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-center-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-center-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-center-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-middle-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-middle-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-middle-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-middle-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/untangle-vertically-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/untangle-vertically-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/untangle-vertically-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/untangle-vertically-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/align-icon-grey-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/align-icon-grey-background.png -------------------------------------------------------------------------------- /AlignTag/Resources/distribute-vertically-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/distribute-vertically-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/distribute-vertically-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/distribute-vertically-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/untangle-horizontally-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/untangle-horizontally-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/untangle-horizontally-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/untangle-horizontally-small.png -------------------------------------------------------------------------------- /AlignTag/Resources/DistributeVerticallyLarge (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/DistributeVerticallyLarge (1).png -------------------------------------------------------------------------------- /AlignTag/Resources/distribute-horizontally-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/distribute-horizontally-large.png -------------------------------------------------------------------------------- /AlignTag/Resources/distribute-horizontally-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonmoreau/align-tag/HEAD/AlignTag/Resources/distribute-horizontally-small.png -------------------------------------------------------------------------------- /AlignTag/CopyToRevitAddinFolder.bat: -------------------------------------------------------------------------------- 1 | copy "AlignTag.addin" "%AppData%\Autodesk\REVIT\Addins\2019" 2 | copy "bin\debug\AlignTag.dll" "%AppData%\Autodesk\REVIT\Addins\2019" 3 | pause -------------------------------------------------------------------------------- /AlignTag/AlignTag.addin: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Application AlignTag 5 | AlignTag\AlignTag.dll 6 | AlignTag.App 7 | 5a9e8c41-09d6-4cdf-8556-38f8119137a4 8 | BM42 9 | BIM 42, http://bim42.com 10 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Simon Moreau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /AlignTag/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("AlignTag")] 9 | [assembly: AssemblyDescription("Align Tags in Revit")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("BIM 42")] 12 | [assembly: AssemblyProduct("Revit Add-In AlignTag")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("321044f7-b0b2-4b1c-af18-e71a19252be0")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.1.0.0")] 36 | [assembly: AssemblyFileVersion("1.1.0.0")] 37 | -------------------------------------------------------------------------------- /AlignTag/PostBuild.ps1: -------------------------------------------------------------------------------- 1 | param ($Configuration, $TargetName, $ProjectDir, $TargetPath, $TargetDir) 2 | write-host $Configuration 3 | write-host $TargetName 4 | write-host $ProjectDir 5 | write-host $TargetPath 6 | write-host $TargetDir 7 | 8 | # sign the dll 9 | $thumbPrint = "e729567d4e9be8ffca04179e3375b7669bccf272" 10 | $cert=Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert | Where { $_.Thumbprint -eq $thumbPrint} 11 | 12 | Set-AuthenticodeSignature -FilePath $TargetPath -Certificate $cert -IncludeChain All -TimestampServer "http://timestamp.comodoca.com/authenticode" 13 | 14 | function CopyToFolder($revitVersion, $addinFolder) { 15 | 16 | if (Test-Path $addinFolder) { 17 | try { 18 | # Remove previous versions 19 | if (Test-Path ($addinFolder + "\" + $TargetName + ".addin")) { Remove-Item ($addinFolder + "\" + $TargetName + ".addin") } 20 | if (Test-Path ($addinFolder + "\" + $TargetName)) { Remove-Item ($addinFolder + "\" + $TargetName) -Recurse } 21 | 22 | # create the folder 23 | New-Item -ItemType Directory -Path ($addinFolder + "\" + $TargetName) -Force 24 | 25 | # Copy the addin file 26 | xcopy /Y ($ProjectDir + $TargetName + ".addin") ($addinFolder) 27 | xcopy /Y ($TargetDir + "\*.dll*") ($addinFolder + "\" + $TargetName) 28 | } 29 | catch { 30 | Write-Host "Something went wrong" 31 | } 32 | } 33 | } 34 | 35 | $revitVersion = $Configuration.replace('Debug','').replace('Release','') 36 | 37 | # Copy to Addin folder for debug 38 | $addinFolder = ($env:APPDATA + "\Autodesk\REVIT\Addins\" + $revitVersion) 39 | CopyToFolder $revitVersion $addinFolder 40 | 41 | # Copy to release folder for building the package 42 | $ReleasePath="G:\My Drive\05 - Travail\Revit Dev\AlignTag\Releases" 43 | $releaseFolder = ($ReleasePath + "\BIM 42 Align.bundle\Contents\" + $revitVersion + "\") 44 | CopyToFolder $revitVersion $releaseFolder 45 | 46 | 47 | ## Zip the package 48 | 49 | $BundleFolder = ($ReleasePath + "\BIM 42 Align.bundle") 50 | 51 | $ReleaseZip = ($ReleasePath + "\" + $TargetName + ".zip") 52 | if (Test-Path $ReleaseZip) { Remove-Item $ReleaseZip } 53 | 54 | if ( Test-Path -Path $ReleasePath ) { 55 | 7z a -tzip $ReleaseZip ($BundleFolder) 56 | } -------------------------------------------------------------------------------- /AlignTag/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace AlignTag.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AlignTag.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Align.Test/Align - Backup.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net48 5 | 6 | false 7 | 8 | 2022Debug;2022Release;2021Debug;2020Debug;2019Debug;2023Debug;2023Release;2021Release;2020Release;2019Release 9 | 10 | 11 | 12 | 13 | $(PROGRAMFILES)\Autodesk\Revit Preview Release\RevitAPI.dll 14 | False 15 | 16 | 17 | $(PROGRAMFILES)\Autodesk\Revit Preview Release\RevitAPIUI.dll 18 | False 19 | 20 | 21 | 22 | $(PROGRAMFILES)\Autodesk\Revit 2023\RevitAPI.dll 23 | False 24 | 25 | 26 | $(PROGRAMFILES)\Autodesk\Revit 2023\RevitAPIUI.dll 27 | False 28 | 29 | 30 | 31 | $(PROGRAMFILES)\Autodesk\Revit 2022\RevitAPI.dll 32 | False 33 | 34 | 35 | $(PROGRAMFILES)\Autodesk\Revit 2022\RevitAPIUI.dll 36 | False 37 | 38 | 39 | 40 | $(PROGRAMFILES)\Autodesk\Revit 2021\RevitAPI.dll 41 | False 42 | 43 | 44 | $(PROGRAMFILES)\Autodesk\Revit 2021\RevitAPIUI.dll 45 | False 46 | 47 | 48 | 49 | $(PROGRAMFILES)\Autodesk\Revit 2020\RevitAPI.dll 50 | False 51 | 52 | 53 | $(PROGRAMFILES)\Autodesk\Revit 2020\RevitAPIUI.dll 54 | False 55 | 56 | 57 | 58 | $(PROGRAMFILES)\Autodesk\Revit 2019\RevitAPI.dll 59 | False 60 | 61 | 62 | $(PROGRAMFILES)\Autodesk\Revit 2019\RevitAPIUI.dll 63 | False 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Align.Test/Align.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net48 5 | 6 | false 7 | 8 | 2022Debug;2022Release;2021Debug;2020Debug;2019Debug;2023Debug;2023Release;2021Release;2020Release;2019Release;2024Debug;2024Release 9 | 10 | 11 | 12 | 13 | $(PROGRAMFILES)\Autodesk\Revit Preview Release\RevitAPI.dll 14 | False 15 | 16 | 17 | $(PROGRAMFILES)\Autodesk\Revit Preview Release\RevitAPIUI.dll 18 | False 19 | 20 | 21 | 22 | $(PROGRAMFILES)\Autodesk\Revit 2023\RevitAPI.dll 23 | False 24 | 25 | 26 | $(PROGRAMFILES)\Autodesk\Revit 2023\RevitAPIUI.dll 27 | False 28 | 29 | 30 | 31 | $(PROGRAMFILES)\Autodesk\Revit 2022\RevitAPI.dll 32 | False 33 | 34 | 35 | $(PROGRAMFILES)\Autodesk\Revit 2022\RevitAPIUI.dll 36 | False 37 | 38 | 39 | 40 | $(PROGRAMFILES)\Autodesk\Revit 2021\RevitAPI.dll 41 | False 42 | 43 | 44 | $(PROGRAMFILES)\Autodesk\Revit 2021\RevitAPIUI.dll 45 | False 46 | 47 | 48 | 49 | $(PROGRAMFILES)\Autodesk\Revit 2020\RevitAPI.dll 50 | False 51 | 52 | 53 | $(PROGRAMFILES)\Autodesk\Revit 2020\RevitAPIUI.dll 54 | False 55 | 56 | 57 | 58 | $(PROGRAMFILES)\Autodesk\Revit 2019\RevitAPI.dll 59 | False 60 | 61 | 62 | $(PROGRAMFILES)\Autodesk\Revit 2019\RevitAPIUI.dll 63 | False 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /AlignTag/AlignExecute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using Autodesk.Revit.Attributes; 5 | using Autodesk.Revit.DB; 6 | using Autodesk.Revit.UI; 7 | using Autodesk.Revit.UI.Selection; 8 | using Autodesk.Revit.DB.Architecture; 9 | using System.Globalization; 10 | using System.Resources; 11 | 12 | namespace AlignTag 13 | { 14 | [Transaction(TransactionMode.Manual)] 15 | class AlignLeft : IExternalCommand 16 | { 17 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 18 | { 19 | Align align = new Align(); 20 | 21 | return align.AlignElements(commandData, ref message, AlignType.Left); 22 | } 23 | } 24 | 25 | [Transaction(TransactionMode.Manual)] 26 | class AlignRight : IExternalCommand 27 | { 28 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 29 | { 30 | Align align = new Align(); 31 | 32 | return align.AlignElements(commandData, ref message, AlignType.Right); 33 | } 34 | } 35 | 36 | [Transaction(TransactionMode.Manual)] 37 | class AlignTop : IExternalCommand 38 | { 39 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 40 | { 41 | Align align = new Align(); 42 | 43 | return align.AlignElements(commandData, ref message, AlignType.Up); 44 | } 45 | } 46 | 47 | [Transaction(TransactionMode.Manual)] 48 | class AlignBottom : IExternalCommand 49 | { 50 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 51 | { 52 | Align align = new Align(); 53 | 54 | return align.AlignElements(commandData, ref message, AlignType.Down); 55 | } 56 | } 57 | 58 | [Transaction(TransactionMode.Manual)] 59 | class AlignCenter : IExternalCommand 60 | { 61 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 62 | { 63 | Align align = new Align(); 64 | 65 | return align.AlignElements(commandData, ref message, AlignType.Center); 66 | } 67 | } 68 | 69 | [Transaction(TransactionMode.Manual)] 70 | class AlignMiddle : IExternalCommand 71 | { 72 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 73 | { 74 | Align align = new Align(); 75 | 76 | return align.AlignElements(commandData, ref message, AlignType.Middle); 77 | } 78 | } 79 | 80 | [Transaction(TransactionMode.Manual)] 81 | class DistributeHorizontally : IExternalCommand 82 | { 83 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 84 | { 85 | Align align = new Align(); 86 | 87 | return align.AlignElements(commandData, ref message, AlignType.Horizontally); 88 | } 89 | } 90 | 91 | [Transaction(TransactionMode.Manual)] 92 | class DistributeVertically : IExternalCommand 93 | { 94 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 95 | { 96 | Align align = new Align(); 97 | 98 | return align.AlignElements(commandData, ref message, AlignType.Vertically); 99 | } 100 | } 101 | 102 | [Transaction(TransactionMode.Manual)] 103 | class UntangleVertically : IExternalCommand 104 | { 105 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 106 | { 107 | Align align = new Align(); 108 | 109 | return align.AlignElements(commandData, ref message, AlignType.UntangleVertically); 110 | } 111 | } 112 | 113 | [Transaction(TransactionMode.Manual)] 114 | class UntangleHorizontally : IExternalCommand 115 | { 116 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 117 | { 118 | Align align = new Align(); 119 | 120 | return align.AlignElements(commandData, ref message, AlignType.UntangleHorizontally); 121 | } 122 | } 123 | 124 | //[Transaction(TransactionMode.Manual)] 125 | //class Arrange : IExternalCommand 126 | //{ 127 | // public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 128 | // { 129 | // Arrange arrange = new Arrange(); 130 | 131 | // return arrange.ArrangeElements(commandData, ref message); 132 | // } 133 | //} 134 | } 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | Align 4 |

5 | 6 |

Align everything in Revit

7 | 8 | # Overview 9 | 10 | The Align tool allows you to align, distribute or organize selected elements, annotations, tags and text along the axis you specify. Furthermore, the Arrange feature will automatically neatly place your tags around the current view. 11 | 12 | The Align plug-in for Autodesk® Revit® can help to save time while producing complex drawings with large sets of annotation. 13 | 14 | Just select a few elements and the Align tool will sort them for you. 15 | 16 | ![Overview](https://raw.githubusercontent.com/simonmoreau/align-tag/master/AlignTag/Resources/alignTags.gif) 17 | 18 | # Getting Started 19 | 20 | Edit _AlignTag.csproj_, and make sure that the following lines a correctly pointing to your Revit installation folder: 21 | * Line 27: $(ProgramW6432)\Autodesk\Revit Preview Release\Revit.exe 22 | * Line 37: $(ProgramW6432)\Autodesk\Revit Preview Release\Revit.exe 23 | * Line 42: $(ProgramW6432)\Autodesk\Revit Preview Release\RevitAPI.dll 24 | * Line 46: $(ProgramW6432)\Autodesk\Revit Preview Release\RevitAPIUI.dll 25 | * Line 140 to 143: ... 26 | 27 | Open the solution in Visual Studio 2017, buid it, and hit "Start" to run Revit in debug mode. 28 | 29 | ## Installation 30 | 31 | There is two ways to install this plugin in Revit: 32 | 33 | ### The easy way 34 | 35 | Download the installer on the [Autodesk App Exchange](https://apps.autodesk.com/RVT/en/Detail/Index?id=2903508825431715905&appLang=en&os=Win32_64) 36 | 37 | ### The (not so) easy way 38 | 39 | You install Align just [like any other Revit add-in](http://help.autodesk.com/view/RVT/2018/ENU/?guid=GUID-4FFDB03E-6936-417C-9772-8FC258A261F7), by copying the add-in manifest (_"AlignTag.addin"_), the assembly DLL (_"AlignTag.dll"_) and the associated help file (_"AlignHelp.chm"_) to the Revit Add-Ins folder (%APPDATA%\Autodesk\Revit\Addins\2018). 40 | 41 | If you specify the full DLL pathname in the add-in manifest, it can also be located elsewhere. However, this DLL, its dependanties and help files must be locted in the same folder. 42 | 43 | Futhermore, the Visual Studio solution contain all the necessary post-build scripts to copy these files into appropriates folders. 44 | 45 | ## Built With 46 | 47 | * .NET Framework 4.7 and [Visual Studio Community](https://www.visualstudio.com/vs/community/) 48 | * The Visual Studio Revit C# and VB add-in templates from [The Building Coder](http://thebuildingcoder.typepad.com/blog/2017/04/revit-2018-visual-studio-c-and-vb-net-add-in-wizards.html) 49 | 50 | # Development 51 | 52 | Want to contribute? Great, I would be happy to integrate your improvements! 53 | 54 | To fix a bug or enhance an existing module, follow these steps: 55 | 56 | * Fork the repo 57 | * Create a new branch (`git checkout -b improve-feature`) 58 | * Make the appropriate changes in the files 59 | * Add changes to reflect the changes made 60 | * Commit your changes (`git commit -am 'Improve feature'`) 61 | * Push to the branch (`git push origin improve-feature`) 62 | * Create a Pull Request 63 | 64 | # Bug / Feature Request 65 | 66 | If you find a bug (connection issue, error while uploading, ...), kindly open an issue [here](https://github.com/simonmoreau/align-tag/issues/new) by including a screenshot of your problem and the expected result. 67 | 68 | If you'd like to request a new function, feel free to do so by opening an issue [here](https://github.com/simonmoreau/align-tag/issues/new). Please include workflows samples and their corresponding results. 69 | 70 | # License 71 | 72 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details 73 | 74 | # Contact information 75 | 76 | This software is an open-source project mostly maintained by myself, Simon Moreau. If you have any questions or request, feel free to contact me at [simon@bim42.com](mailto:simon@bim42.com) or on Twitter [@bim42](https://twitter.com/bim42?lang=en). 77 | 78 | # Revision list 79 | 80 | | **Version Number** | **Version Description** | 81 | | :-------------: |:-------------| 82 | 1.3.0|Add support for every Revit element. Align every element according to its bounding box. Bug fix. Support for Autodesk® Revit® 2018 Version. 83 | 1.2.0|Add support for Text, Keynote Tag, Room and Space Tags. Align every tag according to its bounding box. Add Align Center and Align Middle. Support for Autodesk® Revit® 2017 Version. 84 | 1.1.0|Support for Autodesk® Revit® 2016 Version. Add the Arrange Tags feature. 85 | 1.0.0|First Release 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | Files/ 13 | 14 | # Build results 15 | [Dd]ebug/ 16 | [Dd]ebugPublic/ 17 | [Rr]elease/ 18 | [Rr]eleases/ 19 | x64/ 20 | x86/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | [Ll]og/ 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | project.fragment.lock.json 47 | artifacts/ 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | #*.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.jfm 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # FAKE - F# Make 251 | .fake/ 252 | 253 | # JetBrains Rider 254 | .idea/ 255 | *.sln.iml 256 | 257 | # CodeRush 258 | .cr/ 259 | 260 | # Python Tools for Visual Studio (PTVS) 261 | __pycache__/ 262 | *.pyc -------------------------------------------------------------------------------- /AlignTag.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32421.90 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlignTag", "AlignTag\AlignTag.csproj", "{AB14DB7D-855A-4952-8BD5-752DDDD05D7E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Align.Test", "Align.Test\Align.Test.csproj", "{840BAA3B-473D-4F82-B42A-85F63CFF68AF}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | 2019Debug|Any CPU = 2019Debug|Any CPU 13 | 2019Release|Any CPU = 2019Release|Any CPU 14 | 2020Debug|Any CPU = 2020Debug|Any CPU 15 | 2020Release|Any CPU = 2020Release|Any CPU 16 | 2021Debug|Any CPU = 2021Debug|Any CPU 17 | 2021Release|Any CPU = 2021Release|Any CPU 18 | 2022Debug|Any CPU = 2022Debug|Any CPU 19 | 2022Release|Any CPU = 2022Release|Any CPU 20 | 2023Debug|Any CPU = 2023Debug|Any CPU 21 | 2023Release|Any CPU = 2023Release|Any CPU 22 | 2024Debug|Any CPU = 2024Debug|Any CPU 23 | 2024Release|Any CPU = 2024Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2019Debug|Any CPU.ActiveCfg = 2019Debug|Any CPU 27 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2019Debug|Any CPU.Build.0 = 2019Debug|Any CPU 28 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2019Release|Any CPU.ActiveCfg = 2019Release|Any CPU 29 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2019Release|Any CPU.Build.0 = 2019Release|Any CPU 30 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2020Debug|Any CPU.ActiveCfg = 2020Debug|Any CPU 31 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2020Debug|Any CPU.Build.0 = 2020Debug|Any CPU 32 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2020Release|Any CPU.ActiveCfg = 2020Release|Any CPU 33 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2020Release|Any CPU.Build.0 = 2020Release|Any CPU 34 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2021Debug|Any CPU.ActiveCfg = 2021Debug|Any CPU 35 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2021Debug|Any CPU.Build.0 = 2021Debug|Any CPU 36 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2021Release|Any CPU.ActiveCfg = 2021Release|Any CPU 37 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2021Release|Any CPU.Build.0 = 2021Release|Any CPU 38 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2022Debug|Any CPU.ActiveCfg = 2022Debug|Any CPU 39 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2022Debug|Any CPU.Build.0 = 2022Debug|Any CPU 40 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2022Release|Any CPU.ActiveCfg = 2022Release|Any CPU 41 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2022Release|Any CPU.Build.0 = 2022Release|Any CPU 42 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2023Debug|Any CPU.ActiveCfg = 2023Debug|Any CPU 43 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2023Debug|Any CPU.Build.0 = 2023Debug|Any CPU 44 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2023Release|Any CPU.ActiveCfg = 2023Release|Any CPU 45 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2023Release|Any CPU.Build.0 = 2023Release|Any CPU 46 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2024Debug|Any CPU.ActiveCfg = 2024Debug|Any CPU 47 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2024Debug|Any CPU.Build.0 = 2024Debug|Any CPU 48 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2024Release|Any CPU.ActiveCfg = 2024Release|Any CPU 49 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E}.2024Release|Any CPU.Build.0 = 2024Release|Any CPU 50 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2019Debug|Any CPU.ActiveCfg = 2019Debug|Any CPU 51 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2019Debug|Any CPU.Build.0 = 2019Debug|Any CPU 52 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2019Release|Any CPU.ActiveCfg = 2019Release|Any CPU 53 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2019Release|Any CPU.Build.0 = 2019Release|Any CPU 54 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2020Debug|Any CPU.ActiveCfg = 2020Debug|Any CPU 55 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2020Debug|Any CPU.Build.0 = 2020Debug|Any CPU 56 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2020Release|Any CPU.ActiveCfg = 2020Release|Any CPU 57 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2020Release|Any CPU.Build.0 = 2020Release|Any CPU 58 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2021Debug|Any CPU.ActiveCfg = 2021Debug|Any CPU 59 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2021Debug|Any CPU.Build.0 = 2021Debug|Any CPU 60 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2021Release|Any CPU.ActiveCfg = 2021Release|Any CPU 61 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2021Release|Any CPU.Build.0 = 2021Release|Any CPU 62 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2022Debug|Any CPU.ActiveCfg = 2022Debug|Any CPU 63 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2022Debug|Any CPU.Build.0 = 2022Debug|Any CPU 64 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2022Release|Any CPU.ActiveCfg = 2022Release|Any CPU 65 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2022Release|Any CPU.Build.0 = 2022Release|Any CPU 66 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2023Debug|Any CPU.ActiveCfg = 2023Debug|Any CPU 67 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2023Debug|Any CPU.Build.0 = 2023Debug|Any CPU 68 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2023Release|Any CPU.ActiveCfg = 2023Release|Any CPU 69 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2023Release|Any CPU.Build.0 = 2023Release|Any CPU 70 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2024Debug|Any CPU.ActiveCfg = 2024Debug|Any CPU 71 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2024Debug|Any CPU.Build.0 = 2024Debug|Any CPU 72 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2024Release|Any CPU.ActiveCfg = 2024Release|Any CPU 73 | {840BAA3B-473D-4F82-B42A-85F63CFF68AF}.2024Release|Any CPU.Build.0 = 2024Release|Any CPU 74 | EndGlobalSection 75 | GlobalSection(SolutionProperties) = preSolution 76 | HideSolutionNode = FALSE 77 | EndGlobalSection 78 | GlobalSection(ExtensibilityGlobals) = postSolution 79 | SolutionGuid = {FEE85557-E92A-4887-8A3E-9C7E02DC9C66} 80 | EndGlobalSection 81 | EndGlobal 82 | -------------------------------------------------------------------------------- /AlignTag/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Align.Test/AlignTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Autodesk.Revit.UI; 9 | using Autodesk.Revit.DB; 10 | using Autodesk.Revit.ApplicationServices; 11 | 12 | namespace Align.Test 13 | { 14 | [TestFixture] 15 | public class AlignTest 16 | { 17 | private Document document; 18 | [SetUp] 19 | public void RunBeforeTest(UIApplication uiApplication) 20 | { 21 | string versionName = uiApplication.Application.VersionName.Replace("Autodesk Revit ", ""); 22 | 23 | string path = $"G:\\My Drive\\05 - Travail\\Revit Dev\\AlignTag\\Test Models\\AlignTestModel_{versionName}.rvt"; 24 | UIDocument uIDocument = uiApplication.OpenAndActivateDocument(path); 25 | document = uIDocument.Document; 26 | Console.WriteLine($"Run 'SetUp' in {GetType().Name}"); 27 | Console.WriteLine($"Open the AlignTestModel_{versionName} model."); 28 | } 29 | 30 | [TearDown] 31 | public void RunAfterTest() 32 | { 33 | Console.WriteLine($"Run 'TearDown' in {GetType().Name}"); 34 | } 35 | 36 | [Test] 37 | public void AlignTagLeft() 38 | { 39 | int[] ids = new int[] { 201324, 201325, 201326, 201327 }; 40 | AlignTestFunction(ids, AlignTag.AlignType.Left); 41 | } 42 | 43 | [Test] 44 | public void AlignTagRight() 45 | { 46 | int[] ids = new int[] { 201379, 201380, 201381, 201382 }; 47 | AlignTestFunction(ids, AlignTag.AlignType.Right); 48 | } 49 | 50 | [Test] 51 | public void AlignTagCenter() 52 | { 53 | int[] ids = new int[] { 201439, 201440, 201441, 201442 }; 54 | AlignTestFunction(ids, AlignTag.AlignType.Center); 55 | } 56 | 57 | [Test] 58 | public void AlignTagUp() 59 | { 60 | int[] ids = new int[] { 201483, 201484, 201485, 201486 }; 61 | AlignTestFunction(ids, AlignTag.AlignType.Up); 62 | } 63 | 64 | [Test] 65 | public void AlignTagDown() 66 | { 67 | int[] ids = new int[] { 201556, 201557, 201558, 201559 }; 68 | AlignTestFunction(ids, AlignTag.AlignType.Down); 69 | } 70 | 71 | [Test] 72 | public void AlignTagMiddle() 73 | { 74 | int[] ids = new int[] { 201600, 201601, 201602, 201603 }; 75 | AlignTestFunction(ids, AlignTag.AlignType.Middle); 76 | } 77 | 78 | [Test] 79 | public void DistributeTagHorizontaly() 80 | { 81 | int[] ids = new int[] { 201956, 201957, 201958, 201959 }; 82 | AlignTestFunction(ids, AlignTag.AlignType.Horizontally); 83 | } 84 | 85 | [Test] 86 | public void DistributeTagVerticaly() 87 | { 88 | int[] ids = new int[] { 202057, 202058, 202059, 202060 }; 89 | AlignTestFunction(ids, AlignTag.AlignType.Vertically); 90 | } 91 | 92 | [Test] 93 | public void AlignWallLeft() 94 | { 95 | int[] ids = new int[] { 202240, 202272, 202307, 202356 }; 96 | AlignTestFunction(ids, AlignTag.AlignType.Left); 97 | } 98 | 99 | [Test] 100 | public void AlignWallLeftWithPinned() 101 | { 102 | int[] ids = new int[] { 205004, 205005, 205006, 205007 }; 103 | AlignTestFunction(ids, AlignTag.AlignType.Left); 104 | } 105 | 106 | [Test] 107 | public void AlignWallRight() 108 | { 109 | int[] ids = new int[] { 202393, 202394, 202395, 202396 }; 110 | AlignTestFunction(ids, AlignTag.AlignType.Right); 111 | } 112 | 113 | [Test] 114 | public void AlignWallCenter() 115 | { 116 | int[] ids = new int[] { 202412, 202413, 202414, 202415 }; 117 | AlignTestFunction(ids, AlignTag.AlignType.Center); 118 | } 119 | 120 | [Test] 121 | public void AlignWallUp() 122 | { 123 | int[] ids = new int[] { 202423, 202424, 202425, 202426 }; 124 | AlignTestFunction(ids, AlignTag.AlignType.Up); 125 | } 126 | 127 | [Test] 128 | public void AlignWallDown() 129 | { 130 | int[] ids = new int[] { 202450, 202451, 202452, 202453 }; 131 | AlignTestFunction(ids, AlignTag.AlignType.Down); 132 | } 133 | 134 | [Test] 135 | public void AlignWallMiddle() 136 | { 137 | int[] ids = new int[] { 202459, 202460, 202461, 202462 }; 138 | AlignTestFunction(ids, AlignTag.AlignType.Middle); 139 | } 140 | 141 | [Test] 142 | public void DistributeWallHorizontaly() 143 | { 144 | int[] ids = new int[] { 202532, 202533, 202534, 202535 }; 145 | AlignTestFunction(ids, AlignTag.AlignType.Horizontally); 146 | } 147 | 148 | [Test] 149 | public void DistributeWallVerticaly() 150 | { 151 | int[] ids = new int[] { 202470, 202471, 202472, 202473 }; 152 | AlignTestFunction(ids, AlignTag.AlignType.Vertically); 153 | } 154 | 155 | [Test] 156 | public void AlignRoomTagTop() 157 | { 158 | int[] ids = new int[] { 205282, 205285 }; 159 | AlignTestFunction(ids, AlignTag.AlignType.Up); 160 | } 161 | 162 | [Test] 163 | public void AlignRoomTagBottom() 164 | { 165 | int[] ids = new int[] { 205288, 205291, 205294 }; 166 | AlignTestFunction(ids, AlignTag.AlignType.Down); 167 | } 168 | 169 | [Test] 170 | public void AlignSpaceTagTop() 171 | { 172 | int[] ids = new int[] { 205654, 205655 }; 173 | AlignTestFunction(ids, AlignTag.AlignType.Up); 174 | } 175 | 176 | [Test] 177 | public void AlignSpaceTagBottom() 178 | { 179 | int[] ids = new int[] { 205656, 205657, 205658 }; 180 | AlignTestFunction(ids, AlignTag.AlignType.Down); 181 | } 182 | 183 | [Test] 184 | public void AlignTextNoteRight() 185 | { 186 | int[] ids = new int[] { 205777, 205811, 205819 }; 187 | AlignTestFunction(ids, AlignTag.AlignType.Right); 188 | } 189 | 190 | [Test] 191 | public void AlignTextNoteVerticaly() 192 | { 193 | int[] ids = new int[] { 205832, 205838, 205839 }; 194 | AlignTestFunction(ids, AlignTag.AlignType.Vertically); 195 | } 196 | 197 | private void AlignTestFunction(int[] ids, AlignTag.AlignType alignType) 198 | { 199 | AlignTag.Align align = new AlignTag.Align(); 200 | 201 | ICollection selectedIds = ids.Select(id => new ElementId(id)).ToList(); 202 | 203 | using (TransactionGroup txg = new TransactionGroup(document)) 204 | { 205 | align.AlignTag(alignType, txg, selectedIds, document); 206 | } 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /AlignTag/App.cs: -------------------------------------------------------------------------------- 1 | #region Namespaces 2 | using System; 3 | using System.Collections.Generic; 4 | using Autodesk.Revit.ApplicationServices; 5 | using Autodesk.Revit.Attributes; 6 | using Autodesk.Revit.DB; 7 | using Autodesk.Revit.UI; 8 | using System.Windows.Media; 9 | using System.Windows.Media.Imaging; 10 | using System.Reflection; 11 | using System.IO; 12 | #endregion 13 | 14 | namespace AlignTag 15 | { 16 | class App : IExternalApplication 17 | { 18 | public Result OnStartup(UIControlledApplication a) 19 | { 20 | 21 | //Create the panel for BIM42 Tools 22 | RibbonPanel AlignPanel = a.CreateRibbonPanel("Align"); 23 | 24 | //Create icons in this panel 25 | Icons.CreateIcons(AlignPanel); 26 | 27 | return Result.Succeeded; 28 | } 29 | 30 | public Result OnShutdown(UIControlledApplication a) 31 | { 32 | return Result.Succeeded; 33 | } 34 | } 35 | 36 | class Icons 37 | { 38 | public static void CreateIcons(RibbonPanel bim42Panel) 39 | { 40 | //Retrive dll path 41 | string DllPath = Assembly.GetExecutingAssembly().Location; 42 | 43 | //Create contextual help 44 | string helpPath = "https://witty-river-01a861010.2.azurestaticapps.net/Align/Align.html"; 45 | ContextualHelp help = new ContextualHelp(ContextualHelpType.Url, helpPath); 46 | 47 | 48 | //Add Align Left Button 49 | PushButtonData alignLeftButton = new PushButtonData("alignLeftButton", "Align Left", DllPath, "AlignTag.AlignLeft"); 50 | alignLeftButton.ToolTip = "Align Tags or Elements Left"; 51 | alignLeftButton.LargeImage = RetriveImage("AlignTag.Resources.align-left-large.png"); 52 | alignLeftButton.Image = RetriveImage("AlignTag.Resources.align-left-small.png"); 53 | alignLeftButton.SetContextualHelp(help); 54 | 55 | //Add Align Right Button 56 | PushButtonData alignRightButton = new PushButtonData("alignRightButton", "Align Right", DllPath, "AlignTag.AlignRight"); 57 | alignRightButton.ToolTip = "Align Tags or Elements Right"; 58 | alignRightButton.LargeImage = RetriveImage("AlignTag.Resources.align-right-large.png"); 59 | alignRightButton.Image = RetriveImage("AlignTag.Resources.align-right-small.png"); 60 | alignRightButton.SetContextualHelp(help); 61 | 62 | //Add Align TOp Button 63 | PushButtonData alignTopButton = new PushButtonData("alignTopButton", "Align Top", DllPath, "AlignTag.AlignTop"); 64 | alignTopButton.ToolTip = "Align Tags or Elements Top"; 65 | alignTopButton.LargeImage = RetriveImage("AlignTag.Resources.align-top-large.png"); 66 | alignTopButton.Image = RetriveImage("AlignTag.Resources.align-top-small.png"); 67 | alignTopButton.SetContextualHelp(help); 68 | 69 | //Add Align bottom Button 70 | PushButtonData alignBottomButton = new PushButtonData("alignBottomButton", "Align Bottom", DllPath, "AlignTag.AlignBottom"); 71 | alignBottomButton.ToolTip = "Align Tags or Elements Bottom"; 72 | alignBottomButton.LargeImage = RetriveImage("AlignTag.Resources.align-bottom-large.png"); 73 | alignBottomButton.Image = RetriveImage("AlignTag.Resources.align-bottom-small.png"); 74 | alignBottomButton.SetContextualHelp(help); 75 | 76 | //Add Align Center Button 77 | PushButtonData alignCenterButton = new PushButtonData("alignCenterButton", "Align Center", DllPath, "AlignTag.AlignCenter"); 78 | alignCenterButton.ToolTip = "Align Tags or Elements Center"; 79 | alignCenterButton.LargeImage = RetriveImage("AlignTag.Resources.align-center-large.png"); 80 | alignCenterButton.Image = RetriveImage("AlignTag.Resources.align-center-small.png"); 81 | alignCenterButton.SetContextualHelp(help); 82 | 83 | //Add Align Middle Button 84 | PushButtonData alignMiddleButton = new PushButtonData("alignMiddleButton", "Align Middle", DllPath, "AlignTag.AlignMiddle"); 85 | alignMiddleButton.ToolTip = "Align Tags or Elements Middle"; 86 | alignMiddleButton.LargeImage = RetriveImage("AlignTag.Resources.align-middle-large.png"); 87 | alignMiddleButton.Image = RetriveImage("AlignTag.Resources.align-middle-small.png"); 88 | alignMiddleButton.SetContextualHelp(help); 89 | 90 | //Add Distribute horizontally Button 91 | PushButtonData distributeHorizontallyButton = new PushButtonData("distributeHorizontallyButton", "Distribute\nHorizontally", DllPath, "AlignTag.DistributeHorizontally"); 92 | distributeHorizontallyButton.ToolTip = "Distribute Tags or Elements Horizontally"; 93 | distributeHorizontallyButton.LargeImage = RetriveImage("AlignTag.Resources.distribute-horizontally-large.png"); 94 | distributeHorizontallyButton.Image = RetriveImage("AlignTag.Resources.distribute-horizontally-small.png"); 95 | distributeHorizontallyButton.SetContextualHelp(help); 96 | 97 | //Add Distribute vertically Button 98 | PushButtonData distributeVerticallyButton = new PushButtonData("distributeVerticallyButton", "Distribute\nVertically", DllPath, "AlignTag.DistributeVertically"); 99 | distributeVerticallyButton.ToolTip = "Distribute Tags or Elements Vertically"; 100 | distributeVerticallyButton.LargeImage = RetriveImage("AlignTag.Resources.distribute-vertically-large.png"); 101 | distributeVerticallyButton.Image = RetriveImage("AlignTag.Resources.distribute-vertically-small.png"); 102 | distributeVerticallyButton.SetContextualHelp(help); 103 | 104 | //Add Arrange Button 105 | PushButtonData arrangeButton = new PushButtonData("ArrangeButton", "Arrange\nTags", DllPath, "AlignTag.Arrange"); 106 | arrangeButton.ToolTip = "Arrange Tags around the view"; 107 | arrangeButton.LargeImage = RetriveImage("AlignTag.Resources.arrange-large.png"); 108 | arrangeButton.Image = RetriveImage("AlignTag.Resources.arrange-small.png"); 109 | arrangeButton.SetContextualHelp(help); 110 | 111 | //Add Untangle Vertically Button 112 | PushButtonData untangleVerticallyButton = new PushButtonData("UntangleVerticallyButton", "Untangle\nVertically", DllPath, "AlignTag.UntangleVertically"); 113 | untangleVerticallyButton.ToolTip = "Untangle Vertically Tags or Elements "; 114 | untangleVerticallyButton.LargeImage = RetriveImage("AlignTag.Resources.untangle-vertically-large.png"); 115 | untangleVerticallyButton.Image = RetriveImage("AlignTag.Resources.untangle-vertically-small.png"); 116 | untangleVerticallyButton.SetContextualHelp(help); 117 | 118 | //Add Untangle Horizontally Button 119 | PushButtonData untangleHorizontallyButton = new PushButtonData("UntangleHorizontallyButton", "Untangle\nHorizontally", DllPath, "AlignTag.UntangleHorizontally"); 120 | untangleHorizontallyButton.ToolTip = "Untangle Horizontally Tags or Elements "; 121 | untangleHorizontallyButton.LargeImage = RetriveImage("AlignTag.Resources.untangle-horizontally-large.png"); 122 | untangleHorizontallyButton.Image = RetriveImage("AlignTag.Resources.untangle-horizontally-small.png"); 123 | untangleHorizontallyButton.SetContextualHelp(help); 124 | 125 | bim42Panel.AddStackedItems(alignLeftButton, alignCenterButton, alignRightButton); 126 | bim42Panel.AddStackedItems(alignTopButton, alignMiddleButton, alignBottomButton); 127 | bim42Panel.AddStackedItems(distributeHorizontallyButton, distributeVerticallyButton, arrangeButton); 128 | bim42Panel.AddStackedItems(untangleVerticallyButton, untangleHorizontallyButton); 129 | 130 | } 131 | 132 | private static ImageSource RetriveImage(string imagePath) 133 | { 134 | Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(imagePath); 135 | 136 | switch (imagePath.Substring(imagePath.Length - 3)) 137 | { 138 | case "jpg": 139 | var jpgDecoder = new System.Windows.Media.Imaging.JpegBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); 140 | return jpgDecoder.Frames[0]; 141 | case "bmp": 142 | var bmpDecoder = new System.Windows.Media.Imaging.BmpBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); 143 | return bmpDecoder.Frames[0]; 144 | case "png": 145 | var pngDecoder = new System.Windows.Media.Imaging.PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); 146 | return pngDecoder.Frames[0]; 147 | case "ico": 148 | var icoDecoder = new System.Windows.Media.Imaging.IconBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); 149 | return icoDecoder.Frames[0]; 150 | default: 151 | return null; 152 | } 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /AlignTag/Properties/Resources.Designer (1).cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34014 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace AlignTag.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AlignTag.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap AlignBottomLarge { 67 | get { 68 | object obj = ResourceManager.GetObject("AlignBottomLarge", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap AlignBottomSmall { 77 | get { 78 | object obj = ResourceManager.GetObject("AlignBottomSmall", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Byte[]. 85 | /// 86 | internal static byte[] AlignHelp { 87 | get { 88 | object obj = ResourceManager.GetObject("AlignHelp", resourceCulture); 89 | return ((byte[])(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Bitmap. 95 | /// 96 | internal static System.Drawing.Bitmap AlignLeftLarge { 97 | get { 98 | object obj = ResourceManager.GetObject("AlignLeftLarge", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Looks up a localized resource of type System.Drawing.Bitmap. 105 | /// 106 | internal static System.Drawing.Bitmap AlignLeftSmall { 107 | get { 108 | object obj = ResourceManager.GetObject("AlignLeftSmall", resourceCulture); 109 | return ((System.Drawing.Bitmap)(obj)); 110 | } 111 | } 112 | 113 | /// 114 | /// Looks up a localized resource of type System.Drawing.Bitmap. 115 | /// 116 | internal static System.Drawing.Bitmap AlignRightLarge { 117 | get { 118 | object obj = ResourceManager.GetObject("AlignRightLarge", resourceCulture); 119 | return ((System.Drawing.Bitmap)(obj)); 120 | } 121 | } 122 | 123 | /// 124 | /// Looks up a localized resource of type System.Drawing.Bitmap. 125 | /// 126 | internal static System.Drawing.Bitmap AlignRightSmall { 127 | get { 128 | object obj = ResourceManager.GetObject("AlignRightSmall", resourceCulture); 129 | return ((System.Drawing.Bitmap)(obj)); 130 | } 131 | } 132 | 133 | /// 134 | /// Looks up a localized resource of type System.Drawing.Bitmap. 135 | /// 136 | internal static System.Drawing.Bitmap AlignTopLarge { 137 | get { 138 | object obj = ResourceManager.GetObject("AlignTopLarge", resourceCulture); 139 | return ((System.Drawing.Bitmap)(obj)); 140 | } 141 | } 142 | 143 | /// 144 | /// Looks up a localized resource of type System.Drawing.Bitmap. 145 | /// 146 | internal static System.Drawing.Bitmap AlignTopSmall { 147 | get { 148 | object obj = ResourceManager.GetObject("AlignTopSmall", resourceCulture); 149 | return ((System.Drawing.Bitmap)(obj)); 150 | } 151 | } 152 | 153 | /// 154 | /// Looks up a localized resource of type System.Drawing.Bitmap. 155 | /// 156 | internal static System.Drawing.Bitmap ArrangeLarge { 157 | get { 158 | object obj = ResourceManager.GetObject("ArrangeLarge", resourceCulture); 159 | return ((System.Drawing.Bitmap)(obj)); 160 | } 161 | } 162 | 163 | /// 164 | /// Looks up a localized resource of type System.Drawing.Bitmap. 165 | /// 166 | internal static System.Drawing.Bitmap ArrangeSmall { 167 | get { 168 | object obj = ResourceManager.GetObject("ArrangeSmall", resourceCulture); 169 | return ((System.Drawing.Bitmap)(obj)); 170 | } 171 | } 172 | 173 | /// 174 | /// Looks up a localized resource of type System.Drawing.Bitmap. 175 | /// 176 | internal static System.Drawing.Bitmap DistributeHorizontallyLarge { 177 | get { 178 | object obj = ResourceManager.GetObject("DistributeHorizontallyLarge", resourceCulture); 179 | return ((System.Drawing.Bitmap)(obj)); 180 | } 181 | } 182 | 183 | /// 184 | /// Looks up a localized resource of type System.Drawing.Bitmap. 185 | /// 186 | internal static System.Drawing.Bitmap DistributeHorizontallySmall { 187 | get { 188 | object obj = ResourceManager.GetObject("DistributeHorizontallySmall", resourceCulture); 189 | return ((System.Drawing.Bitmap)(obj)); 190 | } 191 | } 192 | 193 | /// 194 | /// Looks up a localized resource of type System.Drawing.Bitmap. 195 | /// 196 | internal static System.Drawing.Bitmap DistributeVerticallyLarge { 197 | get { 198 | object obj = ResourceManager.GetObject("DistributeVerticallyLarge", resourceCulture); 199 | return ((System.Drawing.Bitmap)(obj)); 200 | } 201 | } 202 | 203 | /// 204 | /// Looks up a localized resource of type System.Drawing.Bitmap. 205 | /// 206 | internal static System.Drawing.Bitmap DistributeVerticallySmall { 207 | get { 208 | object obj = ResourceManager.GetObject("DistributeVerticallySmall", resourceCulture); 209 | return ((System.Drawing.Bitmap)(obj)); 210 | } 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /AlignTag/Tools.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Resources; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using Autodesk.Revit.DB; 8 | using System.IO; 9 | 10 | namespace AlignTag 11 | { 12 | class Tools 13 | { 14 | //Define cultureInfo 15 | public static ResourceManager LangResMan; // declare Resource manager to access to specific cultureinfo 16 | public static CultureInfo Cult; // declare culture info 17 | 18 | public static void GetLocalisationValues() 19 | { 20 | //Create the culture for english 21 | Tools.Cult = CultureInfo.CreateSpecificCulture("en"); 22 | Tools.LangResMan = new System.Resources.ResourceManager("BIM42.Revit2015.Resources.en", System.Reflection.Assembly.GetExecutingAssembly()); 23 | } 24 | 25 | public static double? GetValueFromString(string text) 26 | { 27 | //Check the string value 28 | string heightValueString; 29 | double lenght; 30 | 31 | 32 | if (text.Contains(" mm")) 33 | { 34 | heightValueString = text.Replace(" mm", ""); 35 | } 36 | else if (text.Contains("mm")) 37 | { 38 | heightValueString = text.Replace("mm", ""); 39 | } 40 | else 41 | { 42 | heightValueString = text; 43 | } 44 | 45 | if (double.TryParse(heightValueString, out lenght)) 46 | { 47 | return lenght; 48 | } 49 | else 50 | { 51 | return null; 52 | } 53 | 54 | } 55 | 56 | public static void ExtractRessource(string resourceName, string path) 57 | { 58 | using (Stream input = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) 59 | using (Stream output = File.Create(path)) 60 | { 61 | 62 | // Insert null checking here for production 63 | byte[] buffer = new byte[8192]; 64 | 65 | int bytesRead; 66 | while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) 67 | { 68 | output.Write(buffer, 0, bytesRead); 69 | } 70 | 71 | } 72 | } 73 | 74 | internal static ICollection RevitReferencesToElementIds(Document doc, IList selectedReferences) 75 | { 76 | return selectedReferences.Select(x => doc.GetElement(x).Id).ToList(); 77 | } 78 | 79 | public static void CreateDebuggingSphere(Document doc, XYZ point, string value, Color color) 80 | { 81 | 82 | using (Transaction tx = new Transaction(doc)) 83 | { 84 | 85 | tx.Start("Create Direct Shapes Spheres"); 86 | 87 | Solid solid = CreateSphereAt(point, 0.5); 88 | 89 | CreateDirectShape(doc, solid, color, value + "{" + point.X + "," + point.Y + "," + point.Z + "}"); 90 | 91 | tx.Commit(); 92 | } 93 | } 94 | 95 | public static void DrawInView(Document doc, View view, XYZ point) 96 | { 97 | 98 | // Create a geometry plane 99 | 100 | XYZ origin = view.CropBox.Transform.Inverse.OfPoint(point); 101 | XYZ normal = view.ViewDirection; 102 | 103 | Plane geomPlane = Plane.CreateByNormalAndOrigin(normal, origin); 104 | 105 | //Create a circle 106 | Arc geomCircle = Arc.Create(origin, 10, 0, 2.0 * Math.PI, geomPlane.XVec, geomPlane.YVec); 107 | 108 | 109 | // Create a sketch plane in current document 110 | 111 | SketchPlane.Create(doc, geomPlane); 112 | 113 | // Create a DetailLine element using the 114 | // newly created geometry line and sketch plane 115 | 116 | DetailLine line = doc.Create.NewDetailCurve(view, geomCircle) as DetailLine; 117 | 118 | 119 | } 120 | 121 | public void DrawInViewCoordinates(Document doc) 122 | { 123 | View view = doc.ActiveView; 124 | 125 | //0 of the view 126 | XYZ origin = new XYZ(0, 0, 0); 127 | //In the model reference 128 | XYZ viewOriginInodel = view.CropBox.Transform.OfPoint(origin); 129 | Solid originSolid = CreateSphereAt(viewOriginInodel, 0.1); 130 | CreateDirectShape(doc, originSolid, new Color(0, 255, 0), "View Origin"); 131 | 132 | 133 | origin = new XYZ(1, 0, 0); 134 | //In the model reference 135 | viewOriginInodel = view.CropBox.Transform.OfPoint(origin); 136 | originSolid = CreateSphereAt(viewOriginInodel, 0.1); 137 | CreateDirectShape(doc, originSolid, new Color(0, 255, 0), "View X"); 138 | 139 | origin = new XYZ(0, 1, 0); 140 | //In the model reference 141 | viewOriginInodel = view.CropBox.Transform.OfPoint(origin); 142 | originSolid = CreateSphereAt(viewOriginInodel, 0.1); 143 | CreateDirectShape(doc, originSolid, new Color(0, 255, 0), "View Y"); 144 | 145 | origin = new XYZ(0, 0, 1); 146 | //In the model reference 147 | viewOriginInodel = view.CropBox.Transform.OfPoint(origin); 148 | originSolid = CreateSphereAt(viewOriginInodel, 0.1); 149 | CreateDirectShape(doc, originSolid, new Color(0, 255, 0), "View Z"); 150 | 151 | } 152 | 153 | 154 | 155 | private static void CreateDirectShape(Document doc, Solid solid, Color color, string paramValue) 156 | { 157 | OverrideGraphicSettings ogs = new OverrideGraphicSettings(); 158 | //ogs.SetProjectionFillColor(color); //new Color(0,255,0) 159 | //ogs.SetProjectionFillPatternId(new ElementId(4)); 160 | //ogs.SetProjectionFillPatternVisible(true); 161 | 162 | // create direct shape and assign the sphere shape 163 | DirectShape dsmax = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel)); 164 | 165 | dsmax.ApplicationId = "ApplicationID"; 166 | dsmax.ApplicationDataId = "ApplicationDataId"; 167 | 168 | dsmax.SetShape(new GeometryObject[] { solid }); 169 | doc.ActiveView.SetElementOverrides(dsmax.Id, ogs); 170 | 171 | Parameter parameter = dsmax.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS); 172 | parameter.Set(paramValue); 173 | } 174 | 175 | /// 176 | /// Create and return a solid sphere with 177 | /// a given radius and centre point. 178 | /// 179 | private static Solid CreateSphereAt(XYZ centre, double radius) 180 | { 181 | // Use the standard global coordinate system 182 | // as a frame, translated to the sphere centre. 183 | 184 | Frame frame = new Frame(centre, XYZ.BasisX, XYZ.BasisY, XYZ.BasisZ); 185 | 186 | // Create a vertical half-circle loop; 187 | // this must be in the frame location. 188 | 189 | Arc arc = Arc.Create(centre - radius * XYZ.BasisZ, centre + radius * XYZ.BasisZ, centre + radius * XYZ.BasisX); 190 | 191 | Line line = Line.CreateBound(arc.GetEndPoint(1), arc.GetEndPoint(0)); 192 | 193 | CurveLoop halfCircle = new CurveLoop(); halfCircle.Append(arc); halfCircle.Append(line); 194 | 195 | List loops = new List(1); 196 | 197 | loops.Add(halfCircle); 198 | 199 | return GeometryCreationUtilities.CreateRevolvedGeometry(frame, loops, 0, 2 * Math.PI); 200 | } 201 | 202 | } 203 | 204 | /// 205 | /// Retrive the error message for displaying it in the Revit interface 206 | /// 207 | public class ErrorMessageException : ApplicationException 208 | { 209 | /// 210 | /// constructor entirely using baseclass' 211 | /// 212 | public ErrorMessageException() 213 | : base() 214 | { 215 | } 216 | 217 | /// 218 | /// constructor entirely using baseclass' 219 | /// 220 | /// error message 221 | public ErrorMessageException(String message) 222 | : base(message) 223 | { 224 | } 225 | } 226 | 227 | 228 | /// 229 | /// Manage Warning in the Revit interface 230 | /// 231 | public class TemporaryCommitPreprocessor : IFailuresPreprocessor 232 | { 233 | public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor) 234 | { 235 | IList failList = new List(); 236 | // Inside event handler, get all warnings 237 | failList = failuresAccessor.GetFailureMessages(); 238 | foreach (FailureMessageAccessor failure in failList) 239 | { 240 | // check FailureDefinitionIds against ones that you want to dismiss, 241 | FailureDefinitionId failID = failure.GetFailureDefinitionId(); 242 | // prevent Revit from showing Unenclosed room warnings 243 | if (failID == BuiltInFailures.RoomFailures.RoomTagNotInRoom || 244 | failID == BuiltInFailures.RoomFailures.RoomTagNotInRoomToArea || 245 | failID == BuiltInFailures.RoomFailures.RoomTagNotInRoomToRoom || 246 | failID == BuiltInFailures.RoomFailures.RoomTagNotInRoomToSpace) 247 | { 248 | failuresAccessor.DeleteWarning(failure); 249 | } 250 | } 251 | 252 | return FailureProcessingResult.Continue; 253 | } 254 | } 255 | 256 | } 257 | -------------------------------------------------------------------------------- /AlignTag/AlignTag.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 7 | 8 | 9 | 10 | {AB14DB7D-855A-4952-8BD5-752DDDD05D7E} 11 | Library 12 | Properties 13 | AlignTag 14 | v4.8 15 | 512 16 | 17 | 18 | 19 | true 20 | bin\2024Debug\ 21 | DEBUG;TRACE;Version2024 22 | full 23 | x64 24 | 7.3 25 | prompt 26 | $(PROGRAMFILES)\Autodesk\Revit Preview Release\Revit.exe 27 | Program 28 | 29 | 30 | bin\2024Release\ 31 | TRACE;Version2024 32 | true 33 | pdbonly 34 | x64 35 | 7.3 36 | prompt 37 | $(PROGRAMFILES)\Autodesk\Revit Preview Release\Revit.exe 38 | Program 39 | 40 | 41 | true 42 | bin\2023Debug\ 43 | DEBUG;TRACE;Version2023 44 | full 45 | x64 46 | 7.3 47 | prompt 48 | $(PROGRAMFILES)\Autodesk\Revit 2023\Revit.exe 49 | Program 50 | 51 | 52 | bin\2023Release\ 53 | TRACE;Version2023 54 | true 55 | pdbonly 56 | x64 57 | 7.3 58 | prompt 59 | $(PROGRAMFILES)\Autodesk\Revit 2023\Revit.exe 60 | Program 61 | 62 | 63 | true 64 | bin\2022Debug\ 65 | DEBUG;TRACE;Version2022 66 | full 67 | x64 68 | 7.3 69 | prompt 70 | $(PROGRAMFILES)\Autodesk\Revit 2022\Revit.exe 71 | Program 72 | 73 | 74 | bin\2022Release\ 75 | TRACE;Version2022 76 | true 77 | pdbonly 78 | x64 79 | 7.3 80 | prompt 81 | $(PROGRAMFILES)\Autodesk\Revit 2022\Revit.exe 82 | Program 83 | 84 | 85 | true 86 | bin\2021Debug\ 87 | DEBUG;TRACE;Version2021 88 | full 89 | x64 90 | 7.3 91 | prompt 92 | $(PROGRAMFILES)\Autodesk\Revit 2021\Revit.exe 93 | Program 94 | 95 | 96 | bin\2021Release\ 97 | TRACE;Version2021 98 | true 99 | pdbonly 100 | x64 101 | 7.3 102 | prompt 103 | $(PROGRAMFILES)\Autodesk\Revit 2021\Revit.exe 104 | Program 105 | 106 | 107 | true 108 | bin\2020Debug\ 109 | DEBUG;TRACE;Version2020 110 | full 111 | x64 112 | 7.3 113 | prompt 114 | $(PROGRAMFILES)\Autodesk\Revit 2020\Revit.exe 115 | Program 116 | 117 | 118 | bin\2020Release\ 119 | TRACE;Version2020 120 | true 121 | pdbonly 122 | x64 123 | 7.3 124 | prompt 125 | $(PROGRAMFILES)\Autodesk\Revit 2020\Revit.exe 126 | Program 127 | 128 | 129 | true 130 | bin\2019Debug\ 131 | DEBUG;TRACE;Version2019 132 | full 133 | x64 134 | 7.3 135 | prompt 136 | $(PROGRAMFILES)\Autodesk\Revit 2019\Revit.exe 137 | Program 138 | 139 | 140 | bin\2019Release\ 141 | TRACE;Version2019 142 | true 143 | pdbonly 144 | x64 145 | 7.3 146 | prompt 147 | $(PROGRAMFILES)\Autodesk\Revit 2019\Revit.exe 148 | Program 149 | 150 | 151 | 152 | 153 | $(PROGRAMFILES)\Autodesk\Revit Preview Release\RevitAPI.dll 154 | False 155 | 156 | 157 | $(PROGRAMFILES)\Autodesk\Revit Preview Release\RevitAPIUI.dll 158 | False 159 | 160 | 161 | 162 | $(PROGRAMFILES)\Autodesk\Revit 2023\RevitAPI.dll 163 | False 164 | 165 | 166 | $(PROGRAMFILES)\Autodesk\Revit 2023\RevitAPIUI.dll 167 | False 168 | 169 | 170 | 171 | $(PROGRAMFILES)\Autodesk\Revit 2022\RevitAPI.dll 172 | False 173 | 174 | 175 | $(PROGRAMFILES)\Autodesk\Revit 2022\RevitAPIUI.dll 176 | False 177 | 178 | 179 | 180 | $(PROGRAMFILES)\Autodesk\Revit 2021\RevitAPI.dll 181 | False 182 | 183 | 184 | $(PROGRAMFILES)\Autodesk\Revit 2021\RevitAPIUI.dll 185 | False 186 | 187 | 188 | 189 | $(PROGRAMFILES)\Autodesk\Revit 2020\RevitAPI.dll 190 | False 191 | 192 | 193 | $(PROGRAMFILES)\Autodesk\Revit 2020\RevitAPIUI.dll 194 | False 195 | 196 | 197 | 198 | $(PROGRAMFILES)\Autodesk\Revit 2019\RevitAPI.dll 199 | False 200 | 201 | 202 | $(PROGRAMFILES)\Autodesk\Revit 2019\RevitAPIUI.dll 203 | False 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | True 228 | True 229 | Resources.resx 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | ResXFileCodeGenerator 240 | Resources.Designer.cs 241 | Designer 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | powershell -ExecutionPolicy Unrestricted $(ProjectDir)PostBuild.ps1 -Configuration $(Configuration) -TargetName $(TargetName) -ProjectDir $(ProjectDir) -TargetPath $(TargetPath) -TargetDir $(TargetDir) 278 | 279 | -------------------------------------------------------------------------------- /AlignTag/AnnotationElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Autodesk.Revit.DB; 7 | using Autodesk.Revit.UI; 8 | using Autodesk.Revit.DB.Architecture; 9 | using Autodesk.Revit.DB.Mechanical; 10 | 11 | namespace AlignTag 12 | { 13 | class AnnotationElement 14 | { 15 | public XYZ Center { get; set; } 16 | 17 | public XYZ UpLeft { get; set; } 18 | 19 | public XYZ UpRight { get; set; } 20 | 21 | public XYZ DownLeft { get; set; } 22 | 23 | public XYZ DownRight { get; set; } 24 | 25 | public Element Parent { get; set; } 26 | 27 | private Document _doc; 28 | private View _ownerView; 29 | 30 | public AnnotationElement(PreparationElement preparationElement) 31 | { 32 | Element e = preparationElement.Element; 33 | Parent = e; 34 | _doc = e.Document; 35 | 36 | 37 | if (_doc.GetElement(e.OwnerViewId) != null) 38 | { 39 | _ownerView = _doc.GetElement(e.OwnerViewId) as View; 40 | } 41 | else 42 | { 43 | _ownerView = _doc.ActiveView; 44 | } 45 | 46 | 47 | //Create the view plan 48 | Plane viewPlane = Plane.CreateByNormalAndOrigin(_ownerView.ViewDirection, _ownerView.Origin); 49 | 50 | BoundingBoxXYZ elementBBox = e.get_BoundingBox(_ownerView); 51 | 52 | XYZ globalMax = elementBBox.Max; 53 | XYZ globalMin = elementBBox.Min; 54 | 55 | if (preparationElement.DisplacementVector != null) 56 | { 57 | globalMax = elementBBox.Max - preparationElement.DisplacementVector; 58 | globalMin = elementBBox.Min - preparationElement.DisplacementVector; 59 | } 60 | 61 | double distanceProjected = ProjectedDistance(viewPlane, globalMax, globalMin); 62 | 63 | XYZ alternateMax = new XYZ(globalMax.X, globalMin.Y, globalMax.Z); 64 | XYZ alternateMin = new XYZ(globalMin.X, globalMax.Y, globalMin.Z); 65 | double alternateDistanceProjected = ProjectedDistance(viewPlane, alternateMax, alternateMin); 66 | 67 | if (alternateDistanceProjected > distanceProjected) 68 | { 69 | globalMax = alternateMax; 70 | globalMin = alternateMin; 71 | } 72 | 73 | Transform ownerViewTransform = _ownerView.CropBox.Transform; 74 | XYZ max = ownerViewTransform.Inverse.OfPoint(globalMax); //Max in the coordinate space of the view 75 | XYZ min = ownerViewTransform.Inverse.OfPoint(globalMin); //Min in the coordinate space of the view 76 | 77 | UpLeft = new XYZ(GetMin(min.X, max.X), GetMax(max.Y, min.Y), 0); 78 | UpRight = new XYZ(GetMax(min.X, max.X), GetMax(max.Y, min.Y), 0); 79 | DownLeft = new XYZ(GetMin(min.X, max.X), GetMin(max.Y, min.Y), 0); 80 | DownRight = new XYZ(GetMax(min.X, max.X), GetMin(max.Y, min.Y), 0); 81 | 82 | Center = (UpRight + DownLeft) / 2; 83 | 84 | } 85 | 86 | public override string ToString() 87 | { 88 | string[] Valueparams = new string[] {Parent.Id.ToString(), 89 | UpLeft.X.ToString(), UpLeft.Y.ToString(), UpLeft.Z.ToString(), 90 | UpRight.X.ToString(), UpRight.Y.ToString(), UpRight.Z.ToString(), 91 | DownLeft.X.ToString(), DownLeft.Y.ToString(), DownLeft.Z.ToString(), 92 | DownRight.X.ToString(), DownRight.Y.ToString(), DownRight.Z.ToString(), 93 | Center.X.ToString(), Center.Y.ToString(), Center.Z.ToString() }; 94 | 95 | return String.Join(",", Valueparams); 96 | } 97 | 98 | private double ProjectedDistance(Plane plane, XYZ pointA, XYZ pointB) 99 | { 100 | //To be tested 101 | XYZ UVA = ProjectionOnPlane(pointA, plane); 102 | XYZ UVB = ProjectionOnPlane(pointB, plane); 103 | 104 | return UVA.DistanceTo(UVB); 105 | } 106 | 107 | private XYZ ProjectionOnPlane(XYZ q, Plane plane) 108 | { 109 | //The projection of a point q = (x, y, z) onto a plane given by a point p = (a, b, c) and a normal n = (d, e, f) is 110 | // q_proj = q - dot(q - p, n) * n 111 | 112 | XYZ p = plane.Origin; 113 | XYZ n = plane.Normal.Normalize(); 114 | XYZ q_proj = q - n.Multiply(n.DotProduct(q - p)); 115 | 116 | return q_proj; 117 | } 118 | 119 | private double GetMax(double value1, double value2) 120 | { 121 | if (value1 >= value2) 122 | { 123 | return value1; 124 | } 125 | else 126 | { 127 | return value2; 128 | } 129 | } 130 | 131 | private double GetMin(double value1, double value2) 132 | { 133 | if (value1 >= value2) 134 | { 135 | return value2; 136 | } 137 | else 138 | { 139 | return value1; 140 | } 141 | } 142 | 143 | public XYZ GetLeaderEnd() 144 | { 145 | XYZ LeaderEnd = this.Center; 146 | Element e = this.Parent; 147 | //Find the leader end, if any 148 | if (e.GetType() == typeof(IndependentTag)) 149 | { 150 | IndependentTag tag = e as IndependentTag; 151 | if (tag.HasLeader) 152 | { 153 | if (tag.LeaderEndCondition == LeaderEndCondition.Free) 154 | { 155 | #if Version2022 || Version2023 || Version2024 156 | Reference referencedElement = tag.GetTaggedReferences().FirstOrDefault(); 157 | if (referencedElement != null) LeaderEnd = tag.GetLeaderEnd(referencedElement); 158 | #elif Version2019 || Version2020 || Version2021 159 | LeaderEnd = tag.LeaderEnd; 160 | #endif 161 | } 162 | else 163 | { 164 | Element taggedElement = TagLeader.GetTaggedElement(_doc, tag); 165 | LeaderEnd = TagLeader.GetLeaderEnd(taggedElement, _ownerView); 166 | } 167 | } 168 | } 169 | else if (e.GetType() == typeof(TextNote)) 170 | { 171 | TextNote note = e as TextNote; 172 | if (note.LeaderCount != 0) 173 | { 174 | LeaderEnd = note.GetLeaders().FirstOrDefault().End; 175 | } 176 | 177 | } 178 | else if (e.GetType().IsSubclassOf(typeof(SpatialElementTag))) 179 | { 180 | SpatialElementTag tag = e as SpatialElementTag; 181 | 182 | if (tag.HasLeader) 183 | { 184 | LeaderEnd = tag.LeaderEnd; 185 | } 186 | } 187 | else 188 | { 189 | LeaderEnd = Center; 190 | } 191 | 192 | return LeaderEnd; 193 | } 194 | 195 | public void MoveTo(XYZ point, AlignType alignType) 196 | { 197 | if (!Parent.Pinned) 198 | { 199 | XYZ displacementVector = new XYZ(); 200 | 201 | switch (alignType) 202 | { 203 | case AlignType.Left: 204 | displacementVector = point - UpLeft; 205 | break; 206 | case AlignType.Right: 207 | displacementVector = point - UpRight; 208 | break; 209 | case AlignType.Up: 210 | displacementVector = point - UpRight; 211 | break; 212 | case AlignType.Down: 213 | displacementVector = point - DownRight; 214 | break; 215 | case AlignType.Center: 216 | displacementVector = point - Center; 217 | break; 218 | case AlignType.Middle: 219 | displacementVector = point - Center; 220 | break; 221 | case AlignType.Vertically: 222 | displacementVector = point - Center; 223 | break; 224 | case AlignType.Horizontally: 225 | displacementVector = point - Center; 226 | break; 227 | case AlignType.UntangleVertically: 228 | displacementVector = point - UpLeft; 229 | break; 230 | case AlignType.UntangleHorizontally: 231 | displacementVector = point - UpLeft; 232 | break; 233 | default: 234 | break; 235 | } 236 | 237 | if (!displacementVector.IsAlmostEqualTo(new XYZ(0, 0, 0))) 238 | { 239 | Transform tr = Transform.CreateTranslation(_ownerView.CropBox.Transform.OfVector(displacementVector)); 240 | 241 | if (Parent.GetType() == typeof(IndependentTag)) 242 | { 243 | IndependentTag tag = Parent as IndependentTag; 244 | CustomLeader customLeader = new CustomLeader(); 245 | if (tag.HasLeader && tag.LeaderEndCondition == LeaderEndCondition.Free) 246 | { 247 | #if Version2022 || Version2023 || Version2024 248 | Reference referencedElement = tag.GetTaggedReferences().FirstOrDefault(); 249 | if (referencedElement != null) 250 | { 251 | XYZ leaderEnd = tag.GetLeaderEnd(referencedElement); 252 | customLeader = new CustomLeader(leaderEnd, new XYZ(0, 0, 0)); 253 | } 254 | else 255 | { 256 | customLeader = new CustomLeader(new XYZ(0, 0, 0), new XYZ(0, 0, 0)); 257 | } 258 | 259 | #elif Version2019 || Version2020 || Version2021 260 | customLeader = new CustomLeader(tag.LeaderEnd, new XYZ(0, 0, 0)); 261 | #endif 262 | 263 | } 264 | 265 | tag.TagHeadPosition = tr.OfPoint(tag.TagHeadPosition); 266 | 267 | if (tag.HasLeader && tag.LeaderEndCondition == LeaderEndCondition.Free) 268 | { 269 | #if Version2022 || Version2023 || Version2024 270 | Reference referencedElement = tag.GetTaggedReferences().FirstOrDefault(); 271 | if (referencedElement != null) 272 | { 273 | tag.SetLeaderEnd(referencedElement, customLeader.End); 274 | } 275 | 276 | #elif Version2019 || Version2020 || Version2021 277 | tag.LeaderEnd = customLeader.End; 278 | #endif 279 | 280 | } 281 | 282 | } 283 | else if (Parent.GetType() == typeof(TextNote)) 284 | { 285 | List leaders = new List(); 286 | TextNote note = Parent as TextNote; 287 | if (note.LeaderCount != 0) 288 | { 289 | foreach (Leader leader in note.GetLeaders()) 290 | { 291 | leaders.Add(new CustomLeader(leader)); 292 | } 293 | } 294 | 295 | note.Coord = tr.OfPoint(note.Coord); 296 | 297 | if (leaders.Count != 0) 298 | { 299 | int i = 0; 300 | foreach (Leader leader in note.GetLeaders()) 301 | { 302 | leader.End = leaders[i].End; 303 | leader.Elbow = leaders[i].Elbow; 304 | i++; 305 | } 306 | } 307 | } 308 | else if (Parent.GetType().IsSubclassOf(typeof(SpatialElementTag))) 309 | { 310 | SpatialElementTag spatialElementTag = Parent as SpatialElementTag; 311 | 312 | CustomLeader leader = null; 313 | if (spatialElementTag.HasLeader) 314 | { 315 | leader = new CustomLeader(spatialElementTag.LeaderEnd, new XYZ(0, 0, 0)); 316 | } 317 | 318 | Parent.Location.Move(_ownerView.CropBox.Transform.OfVector(displacementVector)); 319 | 320 | if (!spatialElementTag.IsOrphaned && !spatialElementTag.HasLeader) 321 | { 322 | RoomTag roomTag = spatialElementTag as RoomTag; 323 | if (roomTag != null && !roomTag.Room.IsPointInRoom(roomTag.TagHeadPosition)) 324 | { 325 | spatialElementTag.HasLeader = true; 326 | } 327 | 328 | SpaceTag spaceTag = spatialElementTag as SpaceTag; 329 | if (spaceTag != null && !spaceTag.Space.IsPointInSpace(spaceTag.TagHeadPosition)) 330 | { 331 | spatialElementTag.HasLeader = true; 332 | } 333 | 334 | AreaTag areaTag = spatialElementTag as AreaTag; 335 | if (areaTag != null) 336 | { 337 | areaTag.HasLeader = true; 338 | } 339 | } 340 | 341 | if (leader != null) 342 | { 343 | spatialElementTag.LeaderEnd = leader.End; 344 | } 345 | 346 | 347 | } 348 | else 349 | { 350 | Parent.Location.Move(_ownerView.CropBox.Transform.OfVector(displacementVector)); 351 | } 352 | } 353 | 354 | } 355 | 356 | } 357 | } 358 | 359 | class CustomLeader 360 | { 361 | public XYZ End { get; set; } 362 | public XYZ Elbow { get; set; } 363 | 364 | public CustomLeader(Leader leader) 365 | { 366 | End = leader.End; 367 | Elbow = leader.Elbow; 368 | } 369 | 370 | public CustomLeader() 371 | { 372 | End = new XYZ(0, 0, 0); 373 | Elbow = new XYZ(0, 0, 0); 374 | } 375 | 376 | public CustomLeader(XYZ end, XYZ elbow) 377 | { 378 | End = end; 379 | Elbow = elbow; 380 | } 381 | } 382 | 383 | class PreparationElement 384 | { 385 | public PreparationElement(Element element, XYZ displacementVector) 386 | { 387 | Element = element; 388 | DisplacementVector = displacementVector; 389 | } 390 | public Element Element { get; set; } 391 | public XYZ DisplacementVector { get; set; } 392 | } 393 | 394 | public enum AlignType { Left, Right, Up, Down, Center, Middle, Vertically, Horizontally, UntangleVertically, UntangleHorizontally }; 395 | 396 | class OffsetedElement 397 | { 398 | public OffsetedElement(Element e, XYZ offset) 399 | { 400 | Element = e; 401 | Offset = offset; 402 | } 403 | 404 | public Element Element { get; set; } 405 | public XYZ Offset { get; set; } 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /AlignTag/Align.cs: -------------------------------------------------------------------------------- 1 | #region Namespaces 2 | using System; 3 | using System.Linq; 4 | using System.Collections.Generic; 5 | using Autodesk.Revit.DB; 6 | using Autodesk.Revit.UI; 7 | using Autodesk.Revit.UI.Selection; 8 | using Autodesk.Revit.DB.Architecture; 9 | using Autodesk.Revit.DB.Mechanical; 10 | using System.Diagnostics; 11 | #endregion 12 | 13 | namespace AlignTag 14 | { 15 | public class Align 16 | { 17 | private UIDocument UIdoc; 18 | public Result AlignElements(ExternalCommandData commandData, ref string message, AlignType alignType) 19 | { 20 | // Get the handle of current document. 21 | UIdoc = commandData.Application.ActiveUIDocument; 22 | Document document = UIdoc.Document; 23 | 24 | using (TransactionGroup txg = new TransactionGroup(document)) 25 | { 26 | try 27 | { 28 | ICollection selectedIds = UIdoc.Selection.GetElementIds(); 29 | 30 | bool empty = false; 31 | 32 | if (selectedIds.Count == 0) 33 | { 34 | empty = true; 35 | 36 | IList selectedReferences = UIdoc.Selection.PickObjects(ObjectType.Element, "Pick elements to be aligned"); 37 | selectedIds = Tools.RevitReferencesToElementIds(document, selectedReferences); 38 | UIdoc.Selection.SetElementIds(selectedIds); 39 | } 40 | 41 | AlignTag(alignType, txg, selectedIds, document); 42 | 43 | // Disselect if the selection was empty to begin with 44 | if (empty) selectedIds = new List { ElementId.InvalidElementId }; 45 | 46 | UIdoc.Selection.SetElementIds(selectedIds); 47 | 48 | // Return Success 49 | return Result.Succeeded; 50 | } 51 | 52 | catch (Autodesk.Revit.Exceptions.OperationCanceledException exceptionCanceled) 53 | { 54 | Console.WriteLine(exceptionCanceled.Message); 55 | //message = exceptionCanceled.Message; 56 | if (txg.HasStarted()) 57 | { 58 | txg.RollBack(); 59 | } 60 | return Result.Cancelled; 61 | } 62 | catch (ErrorMessageException errorEx) 63 | { 64 | // checked exception need to show in error messagebox 65 | message = errorEx.Message; 66 | if (txg.HasStarted()) 67 | { 68 | txg.RollBack(); 69 | } 70 | return Result.Failed; 71 | } 72 | catch (Exception ex) 73 | { 74 | // unchecked exception cause command failed 75 | message = ex.Message; 76 | //Trace.WriteLine(ex.ToString()); 77 | if (txg.HasStarted()) 78 | { 79 | txg.RollBack(); 80 | } 81 | return Result.Failed; 82 | } 83 | } 84 | } 85 | 86 | 87 | public void AlignTag(AlignType alignType, TransactionGroup txg, ICollection selectedIds, Document document) 88 | { 89 | 90 | // 1. First Proposed Change 91 | // First check if there is something that's been seleted, and if so - operate on that 92 | // However, if the Uidoc.Slection is empty, prompt for selection. 93 | // This allows you to stay on the 'Add-ins' Tab and keep using the 'Align' tab without going back and forth to 'Modify' 94 | // TO-DO: Should we disselect after we are done? (delete the boolean stuff if you don't think it's worth disselecting) 95 | 96 | using (Transaction tx = new Transaction(document)) 97 | { 98 | txg.Start(AlignTypeToText(alignType)); 99 | 100 | tx.Start("Prepare tags"); 101 | Debug.WriteLine(DateTime.Now.ToString() + " - Start Prepare tags"); 102 | 103 | List annotationElements = RetriveAnnotationElementsFromSelection(document, tx, selectedIds); 104 | 105 | txg.RollBack(); 106 | // txg.Assimilate(); 107 | Debug.WriteLine(DateTime.Now.ToString() + " - Rollback Prepare tags"); 108 | 109 | txg.Start(AlignTypeToText(alignType)); 110 | 111 | tx.Start(AlignTypeToText(alignType)); 112 | Debug.WriteLine(DateTime.Now.ToString() + " - Start align tags"); 113 | 114 | if (annotationElements.Count > 1) 115 | { 116 | AlignAnnotationElements(annotationElements, alignType, document); 117 | } 118 | 119 | Debug.WriteLine(DateTime.Now.ToString() + " - Commit align tags"); 120 | 121 | tx.Commit(); 122 | 123 | txg.Assimilate(); 124 | } 125 | } 126 | 127 | private List RetriveAnnotationElementsFromSelection(Document document, Transaction tx, ICollection ids) 128 | { 129 | List preparationElements = new List(); 130 | 131 | List annotationElements = new List(); 132 | 133 | 134 | //Remove all leader to find the correct tag height and width 135 | foreach (ElementId id in ids) 136 | { 137 | Element e = document.GetElement(id); 138 | 139 | if (e.GetType() == typeof(IndependentTag)) 140 | { 141 | IndependentTag tag = e as IndependentTag; 142 | 143 | //XYZ displacementVector = null; 144 | //if (tag.LeaderEndCondition == LeaderEndCondition.Free) 145 | //{ 146 | // displacementVector = tag.LeaderEnd - tag.TagHeadPosition; 147 | //} 148 | 149 | if (tag.HasLeader) 150 | { 151 | tag.HasLeader = false; 152 | } 153 | 154 | preparationElements.Add(new PreparationElement(e, null)); 155 | 156 | 157 | } 158 | else if (e.GetType() == typeof(TextNote)) 159 | { 160 | TextNote note = e as TextNote; 161 | note.RemoveLeaders(); 162 | preparationElements.Add(new PreparationElement(e, null)); 163 | } 164 | else if (e.GetType().IsSubclassOf(typeof(SpatialElementTag))) 165 | { 166 | SpatialElementTag tag = e as SpatialElementTag; 167 | 168 | XYZ displacementVector = null; 169 | 170 | if (tag.HasLeader) 171 | { 172 | displacementVector = tag.LeaderEnd - tag.TagHeadPosition; 173 | tag.HasLeader = false; 174 | } 175 | 176 | preparationElements.Add(new PreparationElement(e, displacementVector)); 177 | } 178 | else 179 | { 180 | preparationElements.Add(new PreparationElement(e, null)); 181 | } 182 | } 183 | 184 | FailureHandlingOptions options = tx.GetFailureHandlingOptions(); 185 | 186 | options.SetFailuresPreprocessor(new TemporaryCommitPreprocessor()); 187 | // Now, showing of any eventual mini-warnings will be postponed until the following transaction. 188 | tx.Commit(options); 189 | 190 | foreach (PreparationElement e in preparationElements) 191 | { 192 | annotationElements.Add(new AnnotationElement(e)); 193 | } 194 | 195 | return annotationElements; 196 | } 197 | 198 | private void AlignAnnotationElements(List annotationElements, AlignType alignType, Document document) 199 | { 200 | View currentView = document.ActiveView; 201 | XYZ displacementDirection = currentView.UpDirection; 202 | 203 | switch (alignType) 204 | { 205 | case AlignType.Left: 206 | AnnotationElement farthestAnnotation = 207 | annotationElements.OrderBy(x => x.UpLeft.X).FirstOrDefault(); 208 | foreach (AnnotationElement annotationElement in annotationElements) 209 | { 210 | XYZ resultingPoint = new XYZ(farthestAnnotation.UpLeft.X, annotationElement.UpLeft.Y, 0); 211 | annotationElement.MoveTo(resultingPoint, AlignType.Left); 212 | } 213 | break; 214 | case AlignType.Right: 215 | farthestAnnotation = 216 | annotationElements.OrderByDescending(x => x.UpRight.X).FirstOrDefault(); 217 | foreach (AnnotationElement annotationElement in annotationElements) 218 | { 219 | XYZ resultingPoint = new XYZ(farthestAnnotation.UpRight.X, annotationElement.UpRight.Y, 0); 220 | annotationElement.MoveTo(resultingPoint, AlignType.Right); 221 | } 222 | break; 223 | case AlignType.Up: 224 | farthestAnnotation = 225 | annotationElements.OrderByDescending(x => x.UpRight.Y).FirstOrDefault(); 226 | foreach (AnnotationElement annotationElement in annotationElements) 227 | { 228 | XYZ resultingPoint = new XYZ(annotationElement.UpRight.X, farthestAnnotation.UpRight.Y, 0); 229 | annotationElement.MoveTo(resultingPoint, AlignType.Up); 230 | } 231 | break; 232 | case AlignType.Down: 233 | farthestAnnotation = 234 | annotationElements.OrderBy(x => x.DownRight.Y).FirstOrDefault(); 235 | foreach (AnnotationElement annotationElement in annotationElements) 236 | { 237 | XYZ resultingPoint = new XYZ(annotationElement.DownRight.X, farthestAnnotation.DownRight.Y, 0); 238 | annotationElement.MoveTo(resultingPoint, AlignType.Down); 239 | } 240 | break; 241 | case AlignType.Center: //On the same vertical axe 242 | List sortedAnnotationElements = annotationElements.OrderBy(x => x.UpRight.X).ToList(); 243 | AnnotationElement rightAnnotation = sortedAnnotationElements.LastOrDefault(); 244 | AnnotationElement leftAnnotation = sortedAnnotationElements.FirstOrDefault(); 245 | double XCoord = (rightAnnotation.Center.X + leftAnnotation.Center.X) / 2; 246 | foreach (AnnotationElement annotationElement in sortedAnnotationElements) 247 | { 248 | XYZ resultingPoint = new XYZ(XCoord, annotationElement.Center.Y, 0); 249 | annotationElement.MoveTo(resultingPoint, AlignType.Center); 250 | } 251 | break; 252 | case AlignType.Middle: //On the same horizontal axe 253 | sortedAnnotationElements = annotationElements.OrderBy(x => x.UpRight.Y).ToList(); 254 | AnnotationElement upperAnnotation = sortedAnnotationElements.LastOrDefault(); 255 | AnnotationElement lowerAnnotation = sortedAnnotationElements.FirstOrDefault(); 256 | double YCoord = (upperAnnotation.Center.Y + lowerAnnotation.Center.Y) / 2; 257 | foreach (AnnotationElement annotationElement in sortedAnnotationElements) 258 | { 259 | XYZ resultingPoint = new XYZ(annotationElement.Center.X, YCoord, 0); 260 | annotationElement.MoveTo(resultingPoint, AlignType.Middle); 261 | } 262 | break; 263 | case AlignType.Vertically: 264 | sortedAnnotationElements = annotationElements.OrderBy(x => x.UpRight.Y).ToList(); 265 | upperAnnotation = sortedAnnotationElements.LastOrDefault(); 266 | lowerAnnotation = sortedAnnotationElements.FirstOrDefault(); 267 | double spacing = (upperAnnotation.Center.Y - lowerAnnotation.Center.Y) / (annotationElements.Count - 1); 268 | int i = 0; 269 | foreach (AnnotationElement annotationElement in sortedAnnotationElements) 270 | { 271 | XYZ resultingPoint = new XYZ(annotationElement.Center.X, lowerAnnotation.Center.Y + i * spacing, 0); 272 | annotationElement.MoveTo(resultingPoint, AlignType.Vertically); 273 | i++; 274 | } 275 | break; 276 | case AlignType.Horizontally: 277 | sortedAnnotationElements = annotationElements.OrderBy(x => x.UpRight.X).ToList(); 278 | rightAnnotation = sortedAnnotationElements.LastOrDefault(); 279 | leftAnnotation = sortedAnnotationElements.FirstOrDefault(); 280 | spacing = (rightAnnotation.Center.X - leftAnnotation.Center.X) / (annotationElements.Count - 1); 281 | i = 0; 282 | foreach (AnnotationElement annotationElement in sortedAnnotationElements) 283 | { 284 | XYZ resultingPoint = new XYZ(leftAnnotation.Center.X + i * spacing, annotationElement.Center.Y, 0); 285 | annotationElement.MoveTo(resultingPoint, AlignType.Horizontally); 286 | i++; 287 | } 288 | break; 289 | case AlignType.UntangleVertically: 290 | sortedAnnotationElements = annotationElements.OrderBy(y => y.GetLeaderEnd().Y).ToList(); 291 | upperAnnotation = sortedAnnotationElements.FirstOrDefault(); 292 | spacing = 0; 293 | foreach (AnnotationElement annotationElement in sortedAnnotationElements) 294 | { 295 | XYZ resultingPoint = new XYZ(annotationElement.UpLeft.X, upperAnnotation.UpLeft.Y + spacing, 0); 296 | annotationElement.MoveTo(resultingPoint, AlignType.UntangleVertically); 297 | spacing = spacing + (annotationElement.UpLeft.Y - annotationElement.DownLeft.Y); 298 | } 299 | break; 300 | case AlignType.UntangleHorizontally: 301 | sortedAnnotationElements = annotationElements.OrderBy(x => x.GetLeaderEnd().X).ToList(); 302 | leftAnnotation = sortedAnnotationElements.FirstOrDefault(); 303 | spacing = 0; 304 | foreach (AnnotationElement annotationElement in sortedAnnotationElements) 305 | { 306 | XYZ resultingPoint = new XYZ(leftAnnotation.UpLeft.X + spacing, annotationElement.UpLeft.Y, 0); 307 | annotationElement.MoveTo(resultingPoint, AlignType.UntangleHorizontally); 308 | spacing = spacing + (annotationElement.UpRight.X - annotationElement.UpLeft.X); 309 | } 310 | break; 311 | default: 312 | break; 313 | } 314 | } 315 | 316 | private string AlignTypeToText(AlignType alignType) 317 | { 318 | string text = ""; 319 | 320 | switch (alignType) 321 | { 322 | case AlignType.Left: 323 | text = "Align Left"; 324 | break; 325 | case AlignType.Right: 326 | text = "Align Right"; 327 | break; 328 | case AlignType.Up: 329 | text = "Align Top"; 330 | break; 331 | case AlignType.Down: 332 | text = "Align Bottom"; 333 | break; 334 | case AlignType.Center: 335 | text = "Align Center"; 336 | break; 337 | case AlignType.Middle: 338 | text = "Align Middle"; 339 | break; 340 | case AlignType.Vertically: 341 | text = "Distribute Vertically"; 342 | break; 343 | case AlignType.Horizontally: 344 | text = "Distribute Horizontally"; 345 | break; 346 | case AlignType.UntangleVertically: 347 | text = "Untangle Vertically"; 348 | break; 349 | case AlignType.UntangleHorizontally: 350 | text = "Untangle Horizontally"; 351 | break; 352 | default: 353 | break; 354 | } 355 | 356 | return text; 357 | } 358 | 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /AlignTag/Arrange.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Autodesk.Revit.DB; 7 | using Autodesk.Revit.UI; 8 | using Autodesk.Revit.Attributes; 9 | 10 | namespace AlignTag 11 | { 12 | [Transaction(TransactionMode.Manual)] 13 | class Arrange : IExternalCommand 14 | { 15 | public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 16 | { 17 | UIDocument UIdoc = commandData.Application.ActiveUIDocument; 18 | Document doc = UIdoc.Document; 19 | using (TransactionGroup transGroup = new TransactionGroup(doc)) 20 | { 21 | 22 | using (Transaction tx = new Transaction(doc)) 23 | { 24 | try 25 | { 26 | transGroup.Start("Arrange Tags"); 27 | // Add Your Code Here 28 | ArrangeTag(UIdoc, tx); 29 | transGroup.Assimilate(); 30 | // Return Success 31 | return Result.Succeeded; 32 | 33 | } 34 | 35 | catch (Autodesk.Revit.Exceptions.OperationCanceledException exceptionCanceled) 36 | { 37 | message = exceptionCanceled.Message; 38 | if (tx.HasStarted()) 39 | { 40 | tx.RollBack(); 41 | } 42 | return Autodesk.Revit.UI.Result.Cancelled; 43 | } 44 | catch (ErrorMessageException errorEx) 45 | { 46 | // checked exception need to show in error messagebox 47 | message = errorEx.Message; 48 | if (tx.HasStarted()) 49 | { 50 | tx.RollBack(); 51 | } 52 | return Autodesk.Revit.UI.Result.Failed; 53 | } 54 | catch (Exception ex) 55 | { 56 | // unchecked exception cause command failed 57 | message = ex.Message; 58 | //Trace.WriteLine(ex.ToString()); 59 | if (tx.HasStarted()) 60 | { 61 | tx.RollBack(); 62 | } 63 | return Autodesk.Revit.UI.Result.Failed; 64 | } 65 | } 66 | 67 | } 68 | 69 | } 70 | 71 | private void ArrangeTag(UIDocument uidoc, Transaction tx) 72 | { 73 | Document doc = uidoc.Document; 74 | View activeView = doc.ActiveView; 75 | 76 | //Check the current view 77 | if (!activeView.CropBoxActive) 78 | { 79 | throw new ErrorMessageException("Please set a crop box to the view"); 80 | } 81 | 82 | IEnumerable tags = from elem in new FilteredElementCollector(doc, activeView.Id).OfClass(typeof(IndependentTag)).WhereElementIsNotElementType() 83 | let type = elem as IndependentTag 84 | where type.HasLeader == true 85 | select type; 86 | 87 | tx.Start("Prepare Tags"); 88 | 89 | //Remove all leader to find the correct tag height and width 90 | foreach (IndependentTag tag in tags) 91 | { 92 | tag.LeaderEndCondition = LeaderEndCondition.Free; 93 | 94 | #if Version2022 || Version2023 || Version2024 95 | Reference referencedElement = tag.GetTaggedReferences().FirstOrDefault(); 96 | tag.SetLeaderElbow(referencedElement, tag.TagHeadPosition); 97 | #elif Version2019 || Version2020 || Version2021 98 | tag.LeaderEnd = tag.TagHeadPosition; 99 | #endif 100 | 101 | } 102 | 103 | 104 | 105 | tx.Commit(); 106 | tx.Start("Arrange Tags"); 107 | 108 | //Create two lists of TagLeader 109 | List leftTagLeaders = new List(); 110 | List rightTagLeaders = new List(); 111 | 112 | foreach (IndependentTag tag in tags) 113 | { 114 | TagLeader currentTag = new TagLeader(tag, doc); 115 | if (currentTag.Side == ViewSides.Left) 116 | { 117 | leftTagLeaders.Add(currentTag); 118 | } 119 | else 120 | { 121 | rightTagLeaders.Add(currentTag); 122 | } 123 | } 124 | 125 | //Create a list of potential location points for tag headers 126 | List leftTagHeadPoints = CreateTagPositionPoints(activeView, leftTagLeaders, ViewSides.Left); 127 | List rightTagHeadPoints = CreateTagPositionPoints(activeView, rightTagLeaders, ViewSides.Right); 128 | 129 | //Sort tag by Y position 130 | leftTagLeaders = leftTagLeaders.OrderBy(x => x.LeaderEnd.X).ToList(); 131 | leftTagLeaders = leftTagLeaders.OrderBy(x => x.LeaderEnd.Y).ToList(); 132 | 133 | //place and sort 134 | PlaceAndSort(leftTagHeadPoints, leftTagLeaders); 135 | 136 | 137 | //Sort tag by Y position 138 | rightTagLeaders = rightTagLeaders.OrderByDescending(x => x.LeaderEnd.X).ToList(); 139 | rightTagLeaders = rightTagLeaders.OrderBy(x => x.LeaderEnd.Y).ToList(); 140 | 141 | //place and sort 142 | PlaceAndSort(rightTagHeadPoints, rightTagLeaders); 143 | 144 | tx.Commit(); 145 | 146 | } 147 | 148 | private void PlaceAndSort(List positionPoints,List tags) 149 | { 150 | //place TagLeader 151 | foreach (TagLeader tag in tags) 152 | { 153 | XYZ nearestPoint = FindNearestPoint(positionPoints, tag.TagCenter); 154 | tag.TagCenter = nearestPoint; 155 | 156 | //remove this point from the list 157 | positionPoints.Remove(nearestPoint); 158 | } 159 | 160 | //unCross leaders (2 times) 161 | UnCross(tags); 162 | UnCross(tags); 163 | 164 | //update their position 165 | foreach (TagLeader tag in tags) 166 | { 167 | tag.UpdateTagPosition(); 168 | } 169 | } 170 | 171 | private void UnCross(List tags) 172 | { 173 | foreach (TagLeader tag in tags) 174 | { 175 | foreach (TagLeader otherTag in tags) 176 | { 177 | if (tag != otherTag) 178 | { 179 | if (tag.BaseLine.Intersect(otherTag.BaseLine) == SetComparisonResult.Overlap 180 | || tag.BaseLine.Intersect(otherTag.EndLine) == SetComparisonResult.Overlap 181 | || tag.EndLine.Intersect(otherTag.BaseLine) == SetComparisonResult.Overlap 182 | || tag.EndLine.Intersect(otherTag.EndLine) == SetComparisonResult.Overlap) 183 | { 184 | XYZ newPosition = tag.TagCenter; 185 | tag.TagCenter = otherTag.TagCenter; 186 | otherTag.TagCenter = newPosition; 187 | } 188 | } 189 | } 190 | } 191 | } 192 | 193 | private XYZ FindNearestPoint(List points, XYZ basePoint) 194 | { 195 | XYZ nearestPoint = points.FirstOrDefault(); 196 | double nearestDistance = basePoint.DistanceTo(nearestPoint); 197 | double currentDistance = basePoint.DistanceTo(nearestPoint); 198 | 199 | foreach (XYZ point in points) 200 | { 201 | currentDistance = basePoint.DistanceTo(point); 202 | if (currentDistance < nearestDistance) 203 | { 204 | nearestPoint = point; 205 | nearestDistance = basePoint.DistanceTo(point); 206 | } 207 | } 208 | return nearestPoint; 209 | } 210 | 211 | private List CreateTagPositionPoints(View activeView, List tagLeaders, ViewSides side) 212 | { 213 | List points = new List(); 214 | 215 | BoundingBoxXYZ bbox = activeView.CropBox; 216 | 217 | if (tagLeaders.Count != 0) 218 | { 219 | //Get largest tag dimension 220 | double tagHeight = tagLeaders.Max(x => x.TagHeight); 221 | double tagWidth = tagLeaders.Max(x => x.TagWidth); 222 | 223 | double step = tagHeight*1.2; 224 | //double step = (bbox.Max.Y - bbox.Min.Y) / 20; 225 | int max = (int)Math.Round((bbox.Max.Y - bbox.Min.Y) / step); 226 | XYZ baseRight = new XYZ(bbox.Max.X, bbox.Min.Y, 0); 227 | XYZ baseLeft = new XYZ(bbox.Min.X, bbox.Min.Y, 0); 228 | 229 | //create sides points 230 | for (int i = max*2; i > 0; i--) 231 | { 232 | if (side == ViewSides.Left) 233 | { 234 | //Add left point 235 | points.Add(baseLeft + new XYZ(0, step * i, 0)); 236 | } 237 | else 238 | { 239 | //Add right point 240 | points.Add(baseRight + new XYZ(0, step * i, 0)); 241 | } 242 | } 243 | } 244 | 245 | 246 | return points; 247 | } 248 | } 249 | 250 | class TagLeader 251 | { 252 | private Document _doc; 253 | private View _currentView; 254 | private Element _taggedElement; 255 | private IndependentTag _tag; 256 | 257 | public TagLeader(IndependentTag tag, Document doc) 258 | { 259 | _doc = doc; 260 | _currentView = _doc.GetElement(tag.OwnerViewId) as View; 261 | _tag = tag; 262 | 263 | _taggedElement = GetTaggedElement(_doc,_tag); 264 | _tagHeadPosition = _currentView.CropBox.Transform.Inverse.OfPoint(tag.TagHeadPosition); 265 | _tagHeadPosition = new XYZ(_tagHeadPosition.X, _tagHeadPosition.Y, 0); 266 | _leaderEnd = GetLeaderEnd(_taggedElement,_currentView); 267 | 268 | //View center 269 | XYZ viewCenter = (_currentView.CropBox.Max + _currentView.CropBox.Min) / 2; 270 | if (viewCenter.X > _leaderEnd.X) 271 | { 272 | _side = ViewSides.Left; 273 | } 274 | else 275 | { 276 | _side = ViewSides.Right; 277 | } 278 | 279 | GetTagDimension(); 280 | } 281 | 282 | private XYZ _tagHeadPosition; 283 | private XYZ _headOffset; 284 | 285 | private XYZ _tagCenter; 286 | public XYZ TagCenter 287 | { 288 | get { return _tagCenter; } 289 | set 290 | { 291 | _tagCenter = value; 292 | UpdateLeaderPosition(); 293 | } 294 | } 295 | 296 | private Line _endLine; 297 | public Line EndLine 298 | { 299 | get { return _endLine; } 300 | } 301 | 302 | private Line _baseLine; 303 | public Line BaseLine 304 | { 305 | get { return _baseLine; } 306 | } 307 | 308 | private ViewSides _side; 309 | public ViewSides Side 310 | { 311 | get { return _side; } 312 | } 313 | 314 | private XYZ _elbowPosition; 315 | public XYZ ElbowPosition 316 | { 317 | get { return _elbowPosition; } 318 | } 319 | 320 | private void UpdateLeaderPosition() 321 | { 322 | //Update elbow position 323 | XYZ AB = _leaderEnd - _tagCenter; 324 | double mult = AB.X * AB.Y; 325 | mult = mult / Math.Abs(mult); 326 | XYZ delta = new XYZ(AB.X - AB.Y * Math.Tan(mult * Math.PI / 4), 0, 0); 327 | _elbowPosition = _tagCenter + delta; 328 | 329 | //Update lines 330 | if (_leaderEnd.DistanceTo(_elbowPosition)> _doc.Application.ShortCurveTolerance) 331 | { 332 | _endLine = Line.CreateBound(_leaderEnd, _elbowPosition); 333 | } 334 | else 335 | { 336 | _endLine = Line.CreateBound(new XYZ(0, 0, 0), new XYZ(0, 0, 1)); 337 | } 338 | if (_elbowPosition.DistanceTo(_tagCenter) > _doc.Application.ShortCurveTolerance) 339 | { 340 | _baseLine = Line.CreateBound(_elbowPosition, _tagCenter); 341 | } 342 | else 343 | { 344 | _baseLine = Line.CreateBound(new XYZ(0, 0, 0), new XYZ(0, 0, 1)); 345 | } 346 | } 347 | 348 | private XYZ _leaderEnd; 349 | public XYZ LeaderEnd 350 | { 351 | get { return _leaderEnd; } 352 | } 353 | 354 | private double _tagHeight; 355 | public double TagHeight 356 | { 357 | get { return _tagHeight; } 358 | } 359 | 360 | private double _tagWidth; 361 | public double TagWidth 362 | { 363 | get { return _tagWidth; } 364 | } 365 | 366 | private void GetTagDimension() 367 | { 368 | BoundingBoxXYZ bbox = _tag.get_BoundingBox(_currentView); 369 | BoundingBoxXYZ viewBox = _currentView.CropBox; 370 | 371 | _tagHeight = viewBox.Transform.Inverse.OfPoint(bbox.Max).Y - viewBox.Transform.Inverse.OfPoint(bbox.Min).Y; 372 | _tagWidth = viewBox.Transform.Inverse.OfPoint(bbox.Max).X - viewBox.Transform.Inverse.OfPoint(bbox.Min).X; 373 | _tagCenter = (viewBox.Transform.Inverse.OfPoint(bbox.Max) + viewBox.Transform.Inverse.OfPoint(bbox.Min)) / 2; 374 | _tagCenter = new XYZ(_tagCenter.X, _tagCenter.Y, 0); 375 | _headOffset = _tagHeadPosition - _tagCenter; 376 | } 377 | 378 | public static Element GetTaggedElement(Document doc, IndependentTag tag) 379 | { 380 | #if Version2019 || Version2020 || Version2021 381 | LinkElementId linkElementId = tag.TaggedElementId; 382 | #elif Version2022 || Version2023 || Version2024 383 | LinkElementId linkElementId = tag.GetTaggedElementIds().FirstOrDefault(); 384 | #endif 385 | Element taggedElement; 386 | if (linkElementId.HostElementId == ElementId.InvalidElementId) 387 | { 388 | RevitLinkInstance linkInstance = doc.GetElement(linkElementId.LinkInstanceId) as RevitLinkInstance; 389 | Document linkedDocument = linkInstance.GetLinkDocument(); 390 | 391 | taggedElement = linkedDocument.GetElement(linkElementId.LinkedElementId); 392 | } 393 | else 394 | { 395 | taggedElement = doc.GetElement(linkElementId.HostElementId); 396 | } 397 | 398 | return taggedElement; 399 | } 400 | 401 | public static XYZ GetLeaderEnd(Element taggedElement, View currentView) 402 | { 403 | BoundingBoxXYZ bbox = taggedElement.get_BoundingBox(currentView); 404 | BoundingBoxXYZ viewBox = currentView.CropBox; 405 | 406 | //Retrive leader end 407 | XYZ leaderEnd = new XYZ(); 408 | if (bbox != null) 409 | { 410 | leaderEnd = (bbox.Max + bbox.Min) / 2; 411 | } 412 | else 413 | { 414 | leaderEnd = (viewBox.Max + viewBox.Min) / 2 + new XYZ(0.001, 0, 0); 415 | } 416 | 417 | //Get leader end in view reference 418 | leaderEnd = viewBox.Transform.Inverse.OfPoint(leaderEnd); 419 | leaderEnd = new XYZ(Math.Round(leaderEnd.X,4), Math.Round(leaderEnd.Y,4) ,0); 420 | 421 | return leaderEnd; 422 | } 423 | 424 | public void UpdateTagPosition() 425 | { 426 | _tag.LeaderEndCondition = LeaderEndCondition.Attached; 427 | 428 | XYZ offsetFromView = new XYZ(); 429 | if (_side == ViewSides.Left) 430 | { 431 | offsetFromView = new XYZ(-Math.Abs(_tagWidth)*0.5-0.1, 0, 0); 432 | } 433 | else 434 | { 435 | offsetFromView = new XYZ(Math.Abs(_tagWidth )* 0.5+0.1, 0, 0); 436 | } 437 | 438 | 439 | _tag.TagHeadPosition = _currentView.CropBox.Transform.OfPoint(_headOffset + _tagCenter + offsetFromView); 440 | #if Version2022 || Version2023 || Version2024 441 | Reference referencedElement = _tag.GetTaggedReferences().FirstOrDefault(); 442 | _tag.SetLeaderElbow(referencedElement, _currentView.CropBox.Transform.OfPoint(_elbowPosition)); 443 | 444 | #elif Version2019 || Version2020 || Version2021 445 | _tag.LeaderElbow = _currentView.CropBox.Transform.OfPoint(_elbowPosition); 446 | #endif 447 | 448 | } 449 | } 450 | 451 | enum ViewSides { Left, Right }; 452 | } 453 | --------------------------------------------------------------------------------