integrate Revit 2025 SDK

This commit is contained in:
Jeremy Tammik
2024-04-11 13:47:20 +02:00
parent e786953544
commit f414362f2b
889 changed files with 26676 additions and 26865 deletions
@@ -0,0 +1,5 @@
{
"recommendations": [
"ms-dotnettools.csharp",
]
}
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Revit",
"type": "coreclr",
"request": "attach",
"processName": "revit.exe",
"justMyCode": true
}
]
}
@@ -0,0 +1,14 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build"
],
"problemMatcher": "$msCompile"
},
]
}
@@ -0,0 +1,161 @@
//
// (C) Copyright 2003-2008 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//
using System;
using System.Collections.Generic;
using System.Text;
using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical;
using Element = Autodesk.Revit.DB.Element;
namespace CS_AvoidObstruction
{
/// <summary>
/// This class is used to detect the obstructions of a Line or a ray.
/// </summary>
class Detector
{
/// <summary>
/// Revit Document.
/// </summary>
private Document m_rvtDoc;
/// <summary>
/// Revit 3D view.
/// </summary>
private View3D? m_view3d;
/// <summary>
/// Constructor, initialize all the fields.
/// </summary>
/// <param name="rvtDoc">Revit Document</param>
public Detector(Document rvtDoc)
{
m_rvtDoc = rvtDoc;
ElementArray views = new ElementArray();
ElementFilter view3DElementFilter = new ElementClassFilter(typeof(View3D));
FilteredElementCollector collector = new FilteredElementCollector(m_rvtDoc);
collector.WherePasses(view3DElementFilter);
m_view3d = collector.ToElements()[0] as View3D;
}
/// <summary>
/// Return all the obstructions which intersect with a ray given by an origin and a direction.
/// </summary>
/// <param name="origin">Ray's origin</param>
/// <param name="dir">Ray's direction</param>
/// <returns>Obstructions intersected with the given ray</returns>
public List<ReferenceWithContext> Obstructions(XYZ origin, XYZ dir)
{
List<ReferenceWithContext> result = new List<ReferenceWithContext>();
ReferenceIntersector referenceIntersector = new ReferenceIntersector(m_view3d);
referenceIntersector.TargetType = FindReferenceTarget.Face;
var obstructionsOnUnboundLine = referenceIntersector.Find(origin, dir);
foreach (ReferenceWithContext gRef in obstructionsOnUnboundLine)
{
if (!InArray(result, gRef))
{
result.Add(gRef);
}
}
result.Sort(CompareReferences);
return result;
}
/// <summary>
/// Return all the obstructions which intersect with a bound line.
/// </summary>
/// <param name="boundLine">Bound line</param>
/// <returns>Obstructions intersected with the bound line</returns>
public List<ReferenceWithContext> Obstructions(Line boundLine)
{
List<ReferenceWithContext> result = new List<ReferenceWithContext>();
XYZ startPt = boundLine.GetEndPoint(0);
XYZ endPt = boundLine.GetEndPoint(1);
XYZ dir = (endPt.Subtract(startPt)).Normalize();
ReferenceIntersector referenceIntersector = new ReferenceIntersector(m_view3d);
referenceIntersector.TargetType = FindReferenceTarget.Face;
var obstructionsOnUnboundLine = referenceIntersector.Find(startPt, dir);
foreach (ReferenceWithContext gRef in obstructionsOnUnboundLine)
{
Reference refr = gRef.GetReference();
// Judge whether the point is in the bound line or not, if the distance between the point and line
// is Zero, then the point is in the bound line.
if (boundLine.Distance(refr.GlobalPoint) < 1e-9)
{
if (!InArray(result, gRef))
{
result.Add(gRef);
}
}
}
result.Sort(CompareReferences);
return result;
}
/// <summary>
/// Judge whether a given Reference is in a ReferenceWithContext list.
/// Give two ReferenceWithContexts, if their Proximitys and Element Ids are equal,
/// we say the two ReferenceWithContexts are equal.
/// </summary>
/// <param name="arr">ReferenceWithContext Array</param>
/// <param name="entry">ReferenceWithContext</param>
/// <returns>True of false</returns>
private bool InArray(List<ReferenceWithContext> arr, ReferenceWithContext entry)
{
foreach (ReferenceWithContext tmp in arr)
{
if (tmp.Proximity == entry.Proximity &&
tmp.GetReference().ElementId.Value == entry.GetReference().ElementId.Value)
{
return true;
}
}
return false;
}
/// <summary>
/// Used to compare two ReferenceWithContexts, just compare their Proximitys.
/// </summary>
/// <param name="a">First ReferenceWithContext to compare</param>
/// <param name="b">Second ReferenceWithContext to compare</param>
/// <returns>-1, 0, or 1</returns>
private int CompareReferences(ReferenceWithContext a, ReferenceWithContext b)
{
if (a.Proximity > b.Proximity)
{
return 1;
}
if (a.Proximity < b.Proximity)
{
return -1;
}
return 0;
}
}
}
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<Optimize>False</Optimize>
<DebugSymbols>True</DebugSymbols>
<DebugType>Portable</DebugType>
<OutputPath>..\..\Addin\</OutputPath>
<AssemblyName>MacroSamples_MEP</AssemblyName>
<BaseInterMediateOutputPath>obj\</BaseInterMediateOutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<InterMediateOutputPath>obj\Debug</InterMediateOutputPath>
<Deterministic>false</Deterministic>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
</PropertyGroup>
<ItemGroup>
<Reference Include="..\..\..\..\..\RevitAPI.dll">
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\..\..\RevitAPIUI.dll">
<Private>False</Private>
</Reference>
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="del $(OutputPath)\*.dll" />
</Target>
</Project>
@@ -0,0 +1,31 @@
#region Using directives
using System;
using System.Reflection;
using System.Runtime.InteropServices;
#endregion
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NewModule")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NewModule")]
[assembly: AssemblyCopyright("Copyright 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)]
// The assembly version has following format :
//
// Major.Minor.Build.Revision
//
// You can specify all the values or you can use the default the Revision and
// Build Numbers by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]
@@ -0,0 +1,542 @@
//
// (C) Copyright 2003-2017 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//
using System;
using System.Collections.Generic;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.DB.Mechanical;
using System.Diagnostics;
using Element = Autodesk.Revit.DB.Element;
using System.Collections;
namespace CS_AvoidObstruction
{
/// <summary>
/// This class implement the algorithm to detect the obstruction and resolve it.
/// </summary>
class Resolver
{
/// <summary>
/// Revit Document.
/// </summary>
private Document? m_rvtDoc;
/// <summary>
/// Detector to detect the obstructions.
/// </summary>
private Detector m_detector;
PipingSystemType? m_pipingSystemType;
/// <summary>
/// Constructor, initialize all the fields of this class.
/// </summary>
/// <param name="data">Revit ExternalCommandData from external command entrance</param>
public Resolver(Document doc)
{
m_rvtDoc = doc;
m_detector = new Detector(m_rvtDoc);
FilteredElementCollector collector = new FilteredElementCollector(m_rvtDoc);
var pipingSystemTypes = collector.OfClass(typeof(PipingSystemType)).ToElements();
foreach (PipingSystemType pipingSystemType in pipingSystemTypes)
{
if (pipingSystemType.SystemClassification == MEPSystemClassification.SupplyHydronic ||
pipingSystemType.SystemClassification == MEPSystemClassification.ReturnHydronic)
{
m_pipingSystemType = pipingSystemType;
break;
}
}
}
/// <summary>
/// Detect and resolve the obstructions of all Pipes.
/// </summary>
public void Resolve()
{
List<Autodesk.Revit.DB.Element> pipes = new List<Autodesk.Revit.DB.Element>();
FilteredElementCollector collector = new FilteredElementCollector(m_rvtDoc);
pipes.AddRange(collector.OfClass(typeof(Pipe)).ToElements());
foreach (Element pipe in pipes)
{
Resolve(pipe as Pipe);
}
}
/// <summary>
/// Calculate the uniform perpendicular directions with inputting direction "dir".
/// </summary>
/// <param name="dir">Direction to calculate</param>
/// <param name="count">How many perpendicular directions will be calculated</param>
/// <returns>The calculated perpendicular directions with dir</returns>
private List<Autodesk.Revit.DB.XYZ> PerpendicularDirs(Autodesk.Revit.DB.XYZ dir, int count)
{
List<Autodesk.Revit.DB.XYZ> dirs = new List<Autodesk.Revit.DB.XYZ>();
Plane plane = Plane.CreateByNormalAndOrigin(dir, Autodesk.Revit.DB.XYZ.Zero);
Arc arc = Arc.Create(plane, 1.0, 0, 6.28);
double delta = 1.0 / (double)count;
for (int i = 1; i <= count; i++)
{
Autodesk.Revit.DB.XYZ pt = arc.Evaluate(delta * i, true);
dirs.Add(pt);
}
return dirs;
}
/// <summary>
/// Detect the obstructions of pipe and resolve them.
/// </summary>
/// <param name="pipe">Pipe to resolve</param>
private void Resolve(Pipe? pipe)
{
if (pipe == null || m_pipingSystemType == null || m_rvtDoc == null)
return;
var parameter = pipe.get_Parameter(BuiltInParameter.RBS_START_LEVEL_PARAM);
var levelId = parameter.AsElementId();
var systemTypeId = m_pipingSystemType.Id;
// Get the centerline of pipe.
Line? pipeLine = (pipe.Location as LocationCurve)?.Curve as Line;
// Calculate the intersection references with pipe's centerline.
if (pipeLine == null)
return;
List<ReferenceWithContext> obstructionRefArr = m_detector.Obstructions(pipeLine);
// Filter out the references, just allow Pipe, Beam, and Duct.
Filter(pipe, obstructionRefArr);
if (obstructionRefArr.Count == 0)
{
// There are no intersection found.
return;
}
// Calculate the direction of pipe's centerline.
Autodesk.Revit.DB.XYZ dir = pipeLine.GetEndPoint(1) - pipeLine.GetEndPoint(0);
// Build the sections from the intersection references.
List<Section> sections = Section.BuildSections(obstructionRefArr, dir.Normalize());
// Merge the neighbor sections if the distance of them is too close.
for (int i = sections.Count - 2; i >= 0; i--)
{
Autodesk.Revit.DB.XYZ detal = sections[i].End - sections[i + 1].Start;
if (detal.GetLength() < pipe.Diameter * 3)
{
sections[i].Refs.AddRange(sections[i + 1].Refs);
sections.RemoveAt(i + 1);
}
}
// Resolve the obstructions one by one.
foreach (Section sec in sections)
{
Resolve(pipe, sec);
}
// Connect the neighbor sections with pipe and elbow fittings.
//
for (int i = 1; i < sections.Count; i++)
{
// Get the end point from the previous section.
Autodesk.Revit.DB.XYZ start = sections[i - 1].End;
// Get the start point from the current section.
Autodesk.Revit.DB.XYZ end = sections[i].Start;
// Create a pipe between two neighbor section.
Pipe tmpPipe = Pipe.Create(m_rvtDoc, systemTypeId, pipe.PipeType.Id, levelId, start, end);
// Copy pipe's parameters values to tmpPipe.
CopyParameters(pipe, tmpPipe);
// Create elbow fitting to connect previous section with tmpPipe.
Connector? conn1 = FindConnector(sections[i - 1].Pipes[2], start);
Connector? conn2 = FindConnector(tmpPipe, start);
FamilyInstance fi = m_rvtDoc.Create.NewElbowFitting(conn1, conn2);
// Create elbow fitting to connect current section with tmpPipe.
Connector? conn3 = FindConnector(sections[i].Pipes[0], end);
Connector? conn4 = FindConnector(tmpPipe, end);
FamilyInstance f2 = m_rvtDoc.Create.NewElbowFitting(conn3, conn4);
}
// Find two connectors which pipe's two ends connector connected to.
Connector? startConn = FindConnectedTo(pipe, pipeLine.GetEndPoint(0));
Connector? endConn = FindConnectedTo(pipe, pipeLine.GetEndPoint(1));
Pipe? startPipe = null;
if (null != startConn)
{
// Create a pipe between pipe's start connector and pipe's start section.
startPipe = Pipe.Create(m_rvtDoc, pipe.PipeType.Id, levelId, startConn, sections[0].Start);
}
else
{
// Create a pipe between pipe's start point and pipe's start section.
startPipe = Pipe.Create(m_rvtDoc, systemTypeId, pipe.PipeType.Id, levelId, sections[0].Start, pipeLine.GetEndPoint(0));
}
// Copy parameters from pipe to startPipe.
CopyParameters(pipe, startPipe);
// Connect the startPipe and first section with elbow fitting.
Connector? connStart1 = FindConnector(startPipe, sections[0].Start);
Connector? connStart2 = FindConnector(sections[0].Pipes[0], sections[0].Start);
FamilyInstance fii = m_rvtDoc.Create.NewElbowFitting(connStart1, connStart2);
Pipe? endPipe = null;
int count = sections.Count;
if (null != endConn)
{
// Create a pipe between pipe's end connector and pipe's end section.
endPipe = Pipe.Create(m_rvtDoc, pipe.PipeType.Id, levelId, endConn, sections[count - 1].End);
}
else
{
// Create a pipe between pipe's end point and pipe's end section.
endPipe = Pipe.Create(m_rvtDoc, systemTypeId, pipe.PipeType.Id, levelId, sections[count - 1].End, pipeLine.GetEndPoint(1));
}
// Copy parameters from pipe to endPipe.
CopyParameters(pipe, endPipe);
// Connect the endPipe and last section with elbow fitting.
Connector? connEnd1 = FindConnector(endPipe, sections[count - 1].End);
Connector? connEnd2 = FindConnector(sections[count - 1].Pipes[2], sections[count - 1].End);
FamilyInstance fiii = m_rvtDoc.Create.NewElbowFitting(connEnd1, connEnd2);
// Delete the pipe after resolved.
m_rvtDoc.Delete(pipe.Id);
}
/// <summary>
/// Filter the inputting References, just allow Pipe, Duct and Beam References.
/// </summary>
/// <param name="pipe">Pipe</param>
/// <param name="refs">References to filter</param>
private void Filter(Pipe pipe, List<ReferenceWithContext> refs)
{
if (m_rvtDoc == null)
return;
for (int i = refs.Count - 1; i >= 0; i--)
{
Reference cur = refs[i].GetReference();
Element curElem = m_rvtDoc.GetElement(cur);
if (curElem.Id == pipe.Id ||
(!(curElem is Pipe) && !(curElem is Duct) &&
curElem.Category.Id.Value != (int)BuiltInCategory.OST_StructuralFraming))
{
refs.RemoveAt(i);
}
}
}
/// <summary>
/// This method will find out a route to avoid the obstruction.
/// </summary>
/// <param name="pipe">Pipe to resolve</param>
/// <param name="section">Pipe's one obstruction</param>
/// <returns>A route which can avoid the obstruction</returns>
private Line FindRoute(Pipe pipe, Section section)
{
// Perpendicular direction minimal length.
double minLength = pipe.Diameter * 2;
// Parallel direction jump step.
double jumpStep = pipe.Diameter;
// Calculate the directions in which to find the solution.
List<Autodesk.Revit.DB.XYZ> dirs = new List<Autodesk.Revit.DB.XYZ>();
Autodesk.Revit.DB.XYZ? crossDir = null;
foreach (ReferenceWithContext gref in section.Refs)
{
if (m_rvtDoc == null)
continue;
Element elem = m_rvtDoc.GetElement(gref.GetReference());
Line? locationLine = (elem.Location as LocationCurve)?.Curve as Line;
Autodesk.Revit.DB.XYZ refDir = locationLine?.GetEndPoint(1) - locationLine?.GetEndPoint(0);
refDir = refDir.Normalize();
if (refDir.IsAlmostEqualTo(section.PipeCenterLineDirection) || refDir.IsAlmostEqualTo(-section.PipeCenterLineDirection))
{
continue;
}
crossDir = refDir.CrossProduct(section.PipeCenterLineDirection);
dirs.Add(crossDir.Normalize());
break;
}
// When all the obstruction are parallel with the centerline of the pipe,
// We can't calculate the direction from the vector.Cross method.
if (dirs.Count == 0)
{
// Calculate perpendicular directions with dir in four directions.
List<Autodesk.Revit.DB.XYZ> perDirs = PerpendicularDirs(section.PipeCenterLineDirection, 4);
dirs.Add(perDirs[0]);
dirs.Add(perDirs[1]);
}
Line? foundLine = null;
while (null == foundLine)
{
// Extend the section interval by jumpStep.
section.Inflate(0, jumpStep);
section.Inflate(1, jumpStep);
// Find solution in the given directions.
for (int i = 0; null == foundLine && i < dirs.Count; i++)
{
// Calculate the intersections.
List<ReferenceWithContext> obs1 = m_detector.Obstructions(section.Start, dirs[i]);
List<ReferenceWithContext> obs2 = m_detector.Obstructions(section.End, dirs[i]);
// Filter out the intersection result.
Filter(pipe, obs1);
Filter(pipe, obs2);
// Find out the minimal intersections in two opposite direction.
ReferenceWithContext[] mins1 = GetClosestSectionsToOrigin(obs1);
ReferenceWithContext[] mins2 = GetClosestSectionsToOrigin(obs2);
// Find solution in the given direction and its opposite direction.
for (int j = 0; null == foundLine && j < 2; j++)
{
if (mins1[j] != null && Math.Abs(mins1[j].Proximity) < minLength ||
mins2[j] != null && Math.Abs(mins2[j].Proximity) < minLength)
{
continue;
}
// Calculate the maximal height that the parallel line can be reached.
double maxHight = 1000 * pipe.Diameter;
if (mins1[j] != null && mins2[j] != null)
{
maxHight = Math.Min(Math.Abs(mins1[j].Proximity), Math.Abs(mins2[j].Proximity));
}
else if (mins1[j] != null)
{
maxHight = Math.Abs(mins1[j].Proximity);
}
else if (mins2[j] != null)
{
maxHight = Math.Abs(mins2[j].Proximity);
}
Autodesk.Revit.DB.XYZ dir = (j == 1) ? dirs[i] : -dirs[i];
// Calculate the parallel line which can avoid obstructions.
foundLine = FindParallelLine(pipe, section, dir, maxHight);
}
}
}
return foundLine;
}
/// <summary>
/// Find a line Parallel to pipe's centerline to avoid the obstruction.
/// </summary>
/// <param name="pipe">Pipe who has obstructions</param>
/// <param name="section">Pipe's one obstruction</param>
/// <param name="dir">Offset Direction of the parallel line</param>
/// <param name="maxLength">Maximum offset distance</param>
/// <returns>Parallel line which can avoid the obstruction</returns>
private Line? FindParallelLine(Pipe pipe, Section section, Autodesk.Revit.DB.XYZ dir, double maxLength)
{
double step = pipe.Diameter;
double hight = 2 * pipe.Diameter;
while (hight <= maxLength)
{
Autodesk.Revit.DB.XYZ detal = dir * hight;
Line line = Line.CreateBound(section.Start + detal, section.End + detal);
List<ReferenceWithContext> refs = m_detector.Obstructions(line);
Filter(pipe, refs);
if (refs.Count == 0)
{
return line;
}
hight += step;
}
return null;
}
/// <summary>
/// Find out two References, whose ProximityParameter is negative or positive,
/// And Get the minimal value from all positive reference, and get the maximal value
/// from the negative reference. if there are no such reference, using null instead.
/// </summary>
/// <param name="refs">References</param>
/// <returns>Reference array</returns>
private ReferenceWithContext[] GetClosestSectionsToOrigin(List<ReferenceWithContext> refs)
{
ReferenceWithContext[] mins = new ReferenceWithContext[2];
if (refs.Count == 0)
{
return mins;
}
if (refs[0].Proximity > 0)
{
mins[1] = refs[0];
return mins;
}
for (int i = 0; i < refs.Count - 1; i++)
{
if (refs[i].Proximity < 0 && refs[i + 1].Proximity > 0)
{
mins[0] = refs[i];
mins[1] = refs[i + 1];
return mins;
}
}
mins[0] = refs[refs.Count - 1];
return mins;
}
/// <summary>
/// Resolve one obstruction of Pipe.
/// </summary>
/// <param name="pipe">Pipe to resolve</param>
/// <param name="section">One pipe's obstruction</param>
private void Resolve(Pipe pipe, Section section)
{
if (m_rvtDoc == null)
return;
// Find out a parallel line of pipe centerline, which can avoid the obstruction.
Line offset = FindRoute(pipe, section);
// Construct a section line according to the given section.
Line sectionLine = Line.CreateBound(section.Start, section.End);
// Construct two side lines, which can avoid the obstruction too.
Line side1 = Line.CreateBound(sectionLine.GetEndPoint(0), offset.GetEndPoint(0));
Line side2 = Line.CreateBound(offset.GetEndPoint(1), sectionLine.GetEndPoint(1));
//
// Create an "U" shape, which connected with three pipes and two elbows, to round the obstruction.
//
PipeType pipeType = pipe.PipeType;
Autodesk.Revit.DB.XYZ start = side1.GetEndPoint(0);
Autodesk.Revit.DB.XYZ startOffset = offset.GetEndPoint(0);
Autodesk.Revit.DB.XYZ endOffset = offset.GetEndPoint(1);
Autodesk.Revit.DB.XYZ end = side2.GetEndPoint(1);
var parameter = pipe.get_Parameter(BuiltInParameter.RBS_START_LEVEL_PARAM);
var levelId = parameter.AsElementId();
// Create three side pipes of "U" shape.
var systemTypeId = m_pipingSystemType?.Id;
Pipe pipe1 = Pipe.Create(m_rvtDoc, systemTypeId, pipeType.Id, levelId, start, startOffset);
Pipe pipe2 = Pipe.Create(m_rvtDoc, systemTypeId, pipeType.Id, levelId, startOffset, endOffset);
Pipe pipe3 = Pipe.Create(m_rvtDoc, systemTypeId, pipeType.Id, levelId, endOffset, end);
// Copy parameters from pipe to other three created pipes.
CopyParameters(pipe, pipe1);
CopyParameters(pipe, pipe2);
CopyParameters(pipe, pipe3);
// Add the created three pipes to current section.
section.Pipes.Add(pipe1);
section.Pipes.Add(pipe2);
section.Pipes.Add(pipe3);
// Create the first elbow to connect two neighbor pipes of "U" shape.
Connector? conn1 = FindConnector(pipe1, startOffset);
Connector? conn2 = FindConnector(pipe2, startOffset);
m_rvtDoc.Create.NewElbowFitting(conn1, conn2);
// Create the second elbow to connect another two neighbor pipes of "U" shape.
Connector? conn3 = FindConnector(pipe2, endOffset);
Connector? conn4 = FindConnector(pipe3, endOffset);
m_rvtDoc.Create.NewElbowFitting(conn3, conn4);
}
/// <summary>
/// Copy parameters from source pipe to target pipe.
/// </summary>
/// <param name="source">Coping source</param>
/// <param name="target">Coping target</param>
private void CopyParameters(Pipe source, Pipe target)
{
double diameter = source.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).AsDouble();
target.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(diameter);
}
/// <summary>
/// Find out a connector from pipe with a specified point.
/// </summary>
/// <param name="pipe">Pipe to find the connector</param>
/// <param name="conXYZ">Specified point</param>
/// <returns>Connector whose origin is conXYZ</returns>
private Connector? FindConnector(Pipe pipe, Autodesk.Revit.DB.XYZ conXYZ)
{
ConnectorSet conns = pipe.ConnectorManager.Connectors;
foreach (Connector conn in conns)
{
if (conn.Origin.IsAlmostEqualTo(conXYZ))
{
return conn;
}
}
return null;
}
/// <summary>
/// Find out the connector which the pipe's specified connector connected to.
/// The pipe's specified connector is given by point conxyz.
/// </summary>
/// <param name="pipe">Pipe to find the connector</param>
/// <param name="conXYZ">Specified point</param>
/// <returns>Connector whose origin is conXYZ</returns>
private Connector? FindConnectedTo(Pipe pipe, Autodesk.Revit.DB.XYZ conXYZ)
{
Connector? connItself = FindConnector(pipe, conXYZ);
ConnectorSet? connSet = connItself?.AllRefs;
if (connSet == null)
return null;
foreach (Connector conn in connSet)
{
if (conn.Owner.Id.Value != pipe.Id.Value &&
conn.ConnectorType == ConnectorType.End)
{
return conn;
}
}
return null;
}
}
}
@@ -0,0 +1,198 @@
//
// (C) Copyright 2003-2008 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//
using System;
using System.Collections.Generic;
using System.Text;
using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
namespace CS_AvoidObstruction
{
/// <summary>
/// This class presents an obstruction of a Pipe.
/// </summary>
class Section
{
/// <summary>
/// Pipe centerline's direction.
/// </summary>
private XYZ m_dir;
/// <summary>
/// Extend factor in negative direction.
/// </summary>
private double m_startFactor;
/// <summary>
/// Extend factor in positive direction.
/// </summary>
private double m_endFactor;
/// <summary>
/// ReferenceWithContexts contained in this obstruction.
/// </summary>
private List<ReferenceWithContext> m_refs;
/// <summary>
/// Pipes to avoid this obstruction, it is assigned when resolving this obstruction.
/// Its count will be three if resolved, the three pipe constructs a "U" shape to round the obstruction.
/// </summary>
private List<Pipe> m_pipes;
/// <summary>
/// Private constructor, just be called in static factory method BuildSections.
/// </summary>
/// <param name="dir">Pipe's direction</param>
private Section(XYZ dir)
{
m_dir = dir;
m_startFactor = 0;
m_endFactor = 0;
m_refs = new List<ReferenceWithContext>();
m_pipes = new List<Pipe>();
}
/// <summary>
/// Pipe centerline's direction.
/// </summary>
public XYZ PipeCenterLineDirection
{
get { return m_dir; }
}
/// <summary>
/// Pipes to avoid this obstruction, it is assigned when resolving this obstruction.
/// Its count will be three if resolved, the three pipe constructs a "U" shape to round the obstruction.
/// </summary>
public List<Pipe> Pipes
{
get { return m_pipes; }
}
/// <summary>
/// Start point of this obstruction.
/// </summary>
public XYZ Start
{
get
{
return m_refs[0].GetReference().GlobalPoint.Add(m_dir.Multiply(m_startFactor));
}
}
/// <summary>
/// End point of this obstruction.
/// </summary>
public XYZ End
{
get
{
return m_refs[m_refs.Count - 1].GetReference().GlobalPoint.Add(m_dir.Multiply(m_endFactor));
}
}
/// <summary>
/// ReferenceWithContexts contained in this obstruction.
/// </summary>
public List<ReferenceWithContext> Refs
{
get { return m_refs; }
}
/// <summary>
/// Extend this obstruction's interval in one direction.
/// </summary>
/// <param name="index">index of direction, 0 => start, 1 => end</param>
public void Inflate(int index, double value)
{
if (index == 0)
{
m_startFactor -= value;
}
else if (index == 1)
{
m_endFactor += value;
}
else
{
throw new ArgumentOutOfRangeException("Index should be 0 or 1.");
}
}
/// <summary>
/// Build sections for ReferenceWithContexts, it's a factory method to build sections.
/// A section contains several points through which the ray passes the obstruction(s).
/// for example, a section may contain 2 points when the obstruction is stand alone,
/// or contain 4 points if 2 obstructions are intersects with each other in the direction of the ray.
/// </summary>
/// <param name="allrefs">ReferenceWithContexts</param>
/// <param name="dir">Pipe's direction</param>
/// <returns>List of Section</returns>
public static List<Section> BuildSections(List<ReferenceWithContext> allrefs, XYZ dir)
{
List<ReferenceWithContext> buildStack = new List<ReferenceWithContext>();
List<Section> sections = new List<Section>();
Section? current = null;
foreach (ReferenceWithContext geoRef in allrefs)
{
if (buildStack.Count == 0)
{
current = new Section(dir);
sections.Add(current);
}
current?.Refs.Add(geoRef);
ReferenceWithContext? tmp = Find(buildStack, geoRef);
if (tmp != null)
{
buildStack.Remove(tmp);
}
else
buildStack.Add(geoRef);
}
return sections;
}
/// <summary>
/// Judge whether a ReferenceWithContext is already in the list of ReferenceWithContext, return the founded value.
/// </summary>
/// <param name="arr">List of ReferenceWithContext</param>
/// <param name="entry">Reference to test</param>
/// <returns>One ReferenceWithContext has the same element's Id with entry</returns>
private static ReferenceWithContext? Find(List<ReferenceWithContext> arr, ReferenceWithContext entry)
{
foreach (ReferenceWithContext tmp in arr)
{
if (tmp.GetReference().ElementId.Value == entry.GetReference().ElementId.Value)
{
return tmp;
}
}
return null;
}
}
}
@@ -0,0 +1,54 @@
namespace MacroSamples_MEP
{
public sealed partial class ThisApplication : Autodesk.Revit.UI.Macros.ApplicationEntryPoint
{
public event System.EventHandler Startup;
public event System.EventHandler Shutdown;
///
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
private void OnStartup()
{
}
///
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override void FinishInitialization()
{
base.FinishInitialization();
this.OnStartup();
this.InternalStartup();
if ((this.Startup != null))
{
this.Startup(this, System.EventArgs.Empty);
}
}
///
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override void OnShutdown()
{
if ((this.Shutdown != null))
{
this.Shutdown(this, System.EventArgs.Empty);
}
base.OnShutdown();
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override string PrimaryCookie
{
get
{
return "ThisApplication";
}
}
}
}
@@ -0,0 +1,48 @@
using System;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using CS_AvoidObstruction;
namespace MacroSamples_MEP
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.DB.Macros.AddInId("44E3EC19-A71D-402D-8FC0-054D32E35D85")]
public partial class ThisApplication
{
private void Module_Startup(object? sender, EventArgs e)
{
}
private void Module_Shutdown(object? sender, EventArgs e)
{
}
#region Revit Macros generated code
private void InternalStartup()
{
this.Startup += new System.EventHandler(Module_Startup);
this.Shutdown += new System.EventHandler(Module_Shutdown);
}
#endregion
public void AvoidObstruction()
{
using (Transaction trans = new Transaction(this.ActiveUIDocument.Document, "AvoidObstruction"))
{
trans.Start();
Resolver resolver = new Resolver(this.ActiveUIDocument.Document);
resolver.Resolve();
trans.Commit();
}
}
}
}