Hi developers:
Let me brief my demand first:
It is a very safe and convenient way to split a duct or a pipe programmatically.
But I could not find a way to easily split a cable tray or a conduit.
So I did the following job:
So far, everything goes well. I tested it on ducts, pipes, cable trays. But when I do it again on conduits, Revit throws a generation error 😞
Let me share my codes first:
//! *** My codes contain some extention methods. Ther are very easy to understand by their literal names. ***
-------------------- Copy a mep curve element -----------------------
Document doc = elem.Document; Line l = elem.GetLine(); XYZ refRight = elem.GetSelfRight(); XYZ p0 = l.GetEndPoint(0), p1 = l.GetEndPoint(1); Level refLevel = elem.ReferenceLevel; ElementId typeId = elem.GetTypeId(); MEPCurve toReturn; if (elem is Duct || elem is Pipe) { ElementId sysTypeId = elem.MEPSystem.GetTypeId(); if (elem is Duct) // 风管 { var newDuct = Duct.Create(doc, sysTypeId, typeId, refLevel.Id, p0, p1); var newInsulation = newDuct.CopySameInsulationAs(elem as Duct); var newLining = newDuct.CopySameLiningAs(elem as Duct); toReturn = newDuct; } else // 管道 { var newPipe = Pipe.Create(doc, sysTypeId, typeId, refLevel.Id, p0, p1); var newInsulation = newPipe.CopySameInsulationAs(elem as Pipe); toReturn = newPipe; } } else { if (elem is CableTray) // 电缆桥架 { var newCableTray = CableTray.Create(doc, typeId, p0, p1, refLevel.Id); newCableTray.SetWidth(elem.Width); newCableTray.SetHeight(elem.Height); toReturn = newCableTray; } else if (elem is Conduit) // 线管 { var newConduit = Conduit.Create(doc, typeId, p0, p1, refLevel.Id); newConduit.SetDiameter(SizeType.Nominal, elem.Diameter); toReturn = newConduit; } else { throw new NotSupportedException(); } }
--------------------- Self-rotate the new element -----------------------
double ang = refRight.AngleTo(toReturn.GetSelfRight()); using (SubTransaction subTrans = new SubTransaction(doc)) { subTrans.Start(); // 先尝试向一个方向自旋转
// First try rotating in a direction toReturn.Location.Rotate(l, ang); doc.Regenerate(); // 如果新图元自旋转之后与旧图元的自旋转方向不一致,则回滚子事务,并且尝试反向旋转
// Sometimes the rotation is in opposite direction other than we need,
// so I roll it back, and rotate it is another direction. if (toReturn.GetSelfRight().IsSameDirectionAs(refRight)) subTrans.Commit(); else { subTrans.RollBack(); subTrans.Start(); toReturn.Location.Rotate(l, -ang); subTrans.Commit(); } } doc.Regenerate();
------------------ Shrink a straight mep curve element (by setting the end connector)-----------
// shrink is XYZ
SubTransaction subt = new SubTransaction(elem.Document); try { subt.Start();
// IsOppositeDirection(XYZ) is my own ext method, it's easy to understand it :) if (l.Direction.IsOppositeDirection(shrink)) { var conn1 = elem.GetConnectorAt(l.GetEndPoint(1)); conn1.Origin = conn1.Origin + shrink; } else { var conn0 = elem.GetConnectorAt(l.GetEndPoint(0)); // 注意此时的延伸向量与管线的行进方向是反向的 conn0.Origin = conn0.Origin + shrink; } subt.Commit(); return true; } catch { subt.RollBack(); return false; } finally { l.Dispose(); subt.Dispose(); }
Thank you for your patience of reading towards here 🙂
The above mentioned methods work well for ducts, pipes, cable trays, but not conduits.
No I will show some screen shots if the result.
If conduit is a free one (no connection at both ends), it works fine.
If either of it's ends is connected, here comes the trouble:
The error message reads like that there is no available family symbols in the .rvt document.
But my implementation does not require any family instance creation.
I tested it in Revit 2019.
Can anyone help me???? Thanks a lot.
Solved! Go to Solution.
Solved by adam.krug. Go to Solution.
Solved by MarryTookMyCoffe. Go to Solution.
Welcome to Revit Api Where nothing work the same, every thing is exception and Rob can eat a S***.
Therese gonna be a problem like that, a lots of it. I feel your frustration.
I look on it and I see some clues worth trying:
1)try and to conduit a fittings and see what will happen, it is possible that it work like that just for conduit(it woudn't be the first time, they have so much mess with this appie)
2) in my project I have a conduit with Fittings and conduits without Fittings(why they did it like that just puzzle me), maybe it will work for one and not for other.(and I just read about it and it is dead end:
https://forums.autodesk.com/t5/revit-mep-forum/conduit-types/td-p/3412887)
3) I understood that you try to connect them with ConnectTo method, check if .CoordinateSystem.BasisZ are opposite.
4) the most likely reason is that Revit try to make calculation and meanwhile fix all wrong connection.
It can be depressing but my suggestion is to give up and just input fitting, because if you looking for answer why this work like that, you probably not gonna get it, in best case you will get answer that you should connect it with fitting.
First appologize for replying so late. 🙂 I was slowly trying your suggestions.
Actually at first I didn't put your suggesions into my working tasks.
But last night I was trying to debug a new function of our product, which requires breaking an existing mep curve element, as well keep it's original connection.
So the logic, as I posted before, is like the following:
Let's see my code:
Snippet
public static bool ReplaceBy(this Connector oldConn, Connector newConn) { Check.CheckArgumentNull(oldConn); if (newConn == null || !newConn.IsValidObject) return false; if (!oldConn.Origin.IsAlmostEqualTo(newConn.Origin)) return false; if (!oldConn.GetBasisZ().IsSameDirectionAs(newConn.GetBasisZ())) return false; if (!oldConn.IsConnected) return false; var doc = oldConn.Owner.Document; Connector other = oldConn.GetConnected(); other.DisconnectFrom(oldConn); doc.Regenerate(); other.ConnectTo(newConn); doc.Regenerate(); return true; }
========= These were old implementations, which lead to the Conduit bug.
I have already abandoned this old one, and am using the following code:
Snippet
var connAt1ofElem = elem.GetConnectorAt(p1); var connAt1ofNewElem = newElem.GetConnectorAt(p1); if (connAt1ofElem.IsConnected) { var conn = connAt1ofElem.GetConnected(); connAt1ofElem.DisconnectFrom(conn); elem.Document.Create.NewTransitionFitting(connAt1ofNewElem, conn); }
I actually at first just wanted to try my luck by using NewTransitionFitting. Because if the 1st and 2nd connectors share the same dimensions and the same self-rotaion, Revit DOES NOT create a transition, but connect them directly (which makes my programming life a little bit easier)
@MarryTookMyCoffe You told me that "NOTHING WORK THE SAME", so I was thinking that the most unsafe point is ConnectoTo() method. And I kept thinking: how about replacing it with a much safer way NewTransitionFitting()
method.
I tested it in Revit 2019, it worked fine with conduits.
Thank you for your suggesions!
They are greating thoughts which helped me further understand Revit API.
I believe I will still face more little tiny problems in this topic, and I hope we can discuss about it in the future.
One more thing I forgot to share:
I have abandoned the official BreakCurve method supplied by Revit API.
public static ElementId BreakCurve( Document document, ElementId pipeId, XYZ ptBreak )
public static ElementId BreakCurve( Document document, ElementId ductId, XYZ ptBreak )
They are in MechanicalUtils and PlumbingUtils static class.
These two methods lead to a very serious problems: The new element the create can keep connection, but the MEPSystem property is null.
And I cannot programmatically set MEPSystem, so I finally abandoned them.
I am not usre if I understood those two methods correctly, so I posted this question in the forum:
api-breakcurve-method-creates-a-new-duct-pipe-without-mepsystem
I will tell you how it looks, when I was doing similar thing(I made import of pipe installation from other program to Revit).
In beginning I try to use:
NewTransitionFitting,
NewTeeFitting
NewElbowFitting
but there can be a big problem with this in my case NewElbowFitting don't work if ends of pipes are in the same point.
breaking a pipe in half can make your MEPSystem null, I even made some pipes that never had a pipeSystemType(some how).
ConnectoTo() can be unsafe if you try to connect pipes connectors, but with element connector and pipe connector it should be fine (exception is with elementupdater, ConnectoTo() don't work there) .
if you what to make few elbow or transition, command from revit will be fine, but if you what make like 10k of this suckers, it can take a lot of time.
I actually at first just wanted to try my luck by using NewTransitionFitting. Because if the 1st and 2nd connectors share the same dimensions and the same self-rotaion, Revit DOES NOT create a transition, but connect them directly (which makes my programming life a little bit easier)
that method shouldn't work like that. If it leave you with one Conduit or two with connectors connected to each other?(dam maybe they fix something in 2019).
They are in MechanicalUtils and PlumbingUtils static class.
These two methods lead to a very serious problems: The new element the create can keep connection, but the MEPSystem property is null.
did you try regenerate document ? some times it helps.
Finial advice, are you using Revit LookUp? it can make your life so much easier.
@MarryTookMyCoffe Hi there, is it still the case that splitting a conduit would require either duplicating an existing one, resize them and align them, then connect them, or to create another conduit and do similar things after?
Thank you!
Jeff Yao
Hard to tell I'm still working on Revit 2017 and only look up on what methods they delete from new versions. I didn't test connecting and split in new API. I basicly made my own method for this, I need to keep it working with all versions from 2017 to 2020, so many happy change in API is not for me. I know thay change something with transition, but I didn't have time to test it .
hey,
Today I had to tackle the same task and I wrote this helper method.
public static ElementId BreakConduit(Document doc, ElementId conduitId, XYZ breakPoint)
{
var conduit = doc.GetElement(conduitId);
//copy mepCurveToOptimize as newPipe and move to brkPoint
var location = conduit.Location as LocationCurve;
var start = location.Curve.GetEndPoint(0);
var end = location.Curve.GetEndPoint(1);
var copiedEls = ElementTransformUtils.CopyElement(doc, conduit.Id, breakPoint - start);
var newId = copiedEls.First();
//shorten mepCurveToOptimize and newPipe (adjust endpoints)
AdjustMepCurve(conduit, start, breakPoint, false);
AdjustMepCurve(doc.GetElement(newId), breakPoint, end, false);
return newId;
}
public static void AdjustMepCurve(Element mepCurve, XYZ p1, XYZ p2, bool disconnect)
{
if (disconnect)
Disconnect(mepCurve);
var location = mepCurve.Location as LocationCurve;
location.Curve = Line.CreateBound(p1, p2);
}
cheers,
Adam