Hello again,
Well I do not understand exactly why this did not work the first time I tried to use PlaceAsTap() - and granted it still does not give the exact results I want as written, but it is definitely a step in the right direction (see updated code below).
So thank goodness for that.
Thanks again,
Matt
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace ADNIssueExamples.Connectors
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class DisappearingConnectors : IExternalCommand
{
#region Properties
private int _idleFireCount = 0;
///// <summary>
///// Dictionary for holding tap connection information
///// HostPartElementId > HostConnectorID, TapElementId, TapConnectorId
///// </summary>
//private Dictionary<ElementId, List<Tuple<int, ElementId, int>>> _disconnectedTaps;
private Dictionary<ElementId, List<Tuple<int, double, ElementId, int, double, double>>> _disconnectedTaps;
#endregion //Properties
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
var uiApp = commandData.Application;
var uiDoc = uiApp.ActiveUIDocument;
var doc = uiDoc.Document;
var colUserSelectedIds = uiDoc.Selection.GetElementIds();
var lstElementsToCheck = colUserSelectedIds.ToList();
foreach (var elemId in lstElementsToCheck)
{
var fabPart = doc.GetElement(elemId) as FabricationPart;
if (fabPart == null)
{
var fabAssembly = uiDoc.Document.GetElement(elemId) as AssemblyInstance;
if (fabAssembly == null)
{
colUserSelectedIds.Remove(elemId);
continue;
}
fabPart = doc.GetElement(fabAssembly.GetMemberIds().First()) as FabricationPart;
if (fabPart == null)
{
colUserSelectedIds.Remove(elemId);
continue;
}
}
}
if (colUserSelectedIds.Count == 0)
{
TaskDialog.Show("Disappearing Connectors", "Please select at least one fabrication part.");
return Result.Cancelled;
}
try
{
if (!DisconnectTaps(doc, colUserSelectedIds))
{
TaskDialog.Show("Disappearing Connectors", "No taps disconnected, please check selection.");
return Result.Failed;
}
//set selection set for posted command, not needed for example
//var exportCommandId = RevitCommandId.LookupCommandId(_exportCommandString);
//uiApp.PostCommand(exportCommandId);
uiApp.Idling += revit_Idling;
_idleFireCount = 0;
return Result.Succeeded;
}
catch (Exception ex)
{
TaskDialog.Show("Disappearing Connectors", ex.Message);
return Result.Failed;
}
}
private bool DisconnectTaps(Document doc, ICollection<ElementId> colSelectedIds)
{
var tapsDisconnected = false;
// Dictionary for attempting to connect tap by re-placing the tap:
// Key = HostPartElementId Values = HostConnectorID, DistanceToTapConnector, TapElementId, TapConnectorId, TapRoation1, TapRotation2
_disconnectedTaps = new Dictionary<ElementId, List<Tuple<int, double, ElementId, int, double, double>>>();
using (var trans = new Transaction(doc, "Disconnect Taps"))
{
trans.Start();
foreach (ElementId elemId in colSelectedIds)
{
var fabPart = doc.GetElement(elemId) as FabricationPart;
//(Matt) Skip for now if it is a tap, we want to be from the perspective of the duct/pipe
if (fabPart.IsATap() || fabPart.IsAHanger()) continue;
var conManager = fabPart.ConnectorManager;
foreach (Connector con in conManager.Connectors)
{
if (!con.IsConnected) continue;
var connectedConnector = con.AllRefs
.OfType<Connector>()
.First(x => x.IsConnectedTo(con));
// as an experiment, i am leaving the tap at host con id 2 in place,
// because manually snapping the tap connector back on to the straight
// give you a new con id - but apparently if you do it programmatically, it does not
// so that's good news.
//if (connectedConnector.Id == 5) continue;
var connectedPart = connectedConnector.Owner as FabricationPart;
if (connectedPart == null) continue;
if (!connectedPart.IsATap()) continue;
var hostLocationCurve = fabPart.Location as LocationCurve;
var hostCenterLine = hostLocationCurve.Curve;
var tap = connectedPart;
var tapCon = connectedConnector;
// For some reason this calculation is not working....
//var a = hostCenterLine.Distance(tapCon.Origin); ;
//var c = (tapCon.Origin - con.Origin).GetLength();
//var b = Math.Sqrt((Math.Pow(c, 2) - Math.Pow(a, 2)));
//var distanceToTapCon = b;
// So let's try halfway down the straight.
var distanceToTapCon = (con.Origin - hostCenterLine.Evaluate(0.5, true)).GetLength();
//turns out this is not giving me the halfway point, but it did work as a valid distance.
var data = new Tuple<int, double, ElementId, int, double, double>
( 0, distanceToTapCon, tap.Id, tapCon.Id, 0.0, 0.0);
if (_disconnectedTaps.ContainsKey(fabPart.Id))
{
_disconnectedTaps[fabPart.Id].Add(data);
}
else
{
_disconnectedTaps.Add(fabPart.Id, new List<Tuple<int, double, ElementId, int, double, double>>());
_disconnectedTaps[fabPart.Id].Add(data);
}
con.DisconnectFrom(tapCon);
tapsDisconnected = true;
}
}
trans.Commit();
}
return tapsDisconnected;
}
public void revit_Idling(object sender, Autodesk.Revit.UI.Events.IdlingEventArgs e)
{
//(Matt) the posted command doesn't fire until after the first idle
if (_idleFireCount == 0)
{
_idleFireCount += 1;
return;
}
var uiApp = sender as UIApplication;
var uiDoc = uiApp.ActiveUIDocument;
var doc = uiDoc.Document;
try
{
ReconnectTaps(doc);
}
catch (Exception ex)
{
TaskDialog.Show("Disappearing Connectors", ex.Message);
}
finally
{
uiApp.Idling -= revit_Idling;
}
}
private void ReconnectTaps(Document doc)
{
using (var trans = new Transaction(doc, "Reconnect Taps"))
{
trans.Start();
foreach (var entry in _disconnectedTaps)
{
foreach (var data in entry.Value)
{
var hostPart = doc.GetElement(entry.Key) as FabricationPart;
var hostConManager = hostPart.ConnectorManager;
var hostConCnt = hostConManager.Connectors.Size;
var hostUnusedCnt = hostConManager.UnusedConnectors != null ? hostConManager.UnusedConnectors.Size : 0;
var hostCon = hostConManager.Lookup(data.Item1);
var distanceToTapCon = data.Item2;
var tap = doc.GetElement(data.Item3) as FabricationPart;
var tapConManager = tap.ConnectorManager;
var tapConCnt = tapConManager.Connectors.Size;
var tapUnusedCnt = tapConManager.UnusedConnectors != null ? tapConManager.UnusedConnectors.Size : 0;
var tapCon = tapConManager.Lookup(data.Item4);
var tapRotation1 = data.Item5;
var tapRotation2 = data.Item6;
if (hostCon == null)
throw new Exception("Unable to reconnect tap, host connector missing:\n\n"
+ "Host part id = " + hostPart.Id.IntegerValue.ToString() + "\n"
+ "Host conn id = " + data.Item1 + "\n"
+ "Host conn cnt = " + hostConCnt + "\n"
+ "Host Unused cnt = " + hostUnusedCnt + "\n\n"
+ "Tap part id = " + tap.Id.IntegerValue.ToString() + "\n"
+ "Tap conn id = " + data.Item3 + "\n"
+ "Tap conn cnt = " + tapConCnt + "\n"
+ "Tap Unused cnt = " + tapUnusedCnt);
FabricationPart.PlaceAsTap(doc, tapCon, hostCon, distanceToTapCon, tapRotation1, tapRotation2);
//if (System.Diagnostics.Debugger.IsAttached)
//{
// Debug.Assert(hostConManager.Connectors.Size == hostConCnt + 1, "Tap not connected...");
//}
//the tap was placed, but without the correct rotations or distance down the straight.
//still, this fixes the original problem.
}
}
trans.Commit();
}
}
}
}