| name | c3d-pipe-networks |
| description | Gravity/pressure pipe networks — pipes, structures, parts lists, labels, styles |
Civil 3D Pipe Networks
Use this skill when working with gravity or pressure pipe networks - creating networks, pipes, structures, labels, styles, or interference checks.
Accessing Pipe Networks
CivilDocument doc = CivilApplication.ActiveDocument;
ObjectIdCollection networkIds = doc.GetPipeNetworkIds();
foreach (ObjectId netId in networkIds)
{
Network network = ts.GetObject(netId, OpenMode.ForRead) as Network;
ed.WriteMessage("Network: {0}\n", network.Name);
}
ObjectIdCollection pressureNetworkIds = doc.GetPressurePipeNetworkIds();
foreach (ObjectId netId in pressureNetworkIds)
{
PressurePipeNetwork pressNet = ts.GetObject(netId, OpenMode.ForRead) as PressurePipeNetwork;
ObjectIdCollection pipeIds = pressNet.GetPipeIds();
ObjectIdCollection fittingIds = pressNet.GetFittingIds();
ObjectIdCollection appurtIds = pressNet.GetAppurtenanceIds();
}
Pipe-Specific Ambient Settings
SettingsPipeNetwork oSettings =
doc.Settings.GetSettings<SettingsPipeNetwork>() as SettingsPipeNetwork;
ed.WriteMessage("Pipe rules: {0}\n", oSettings.Rules.Pipe.Value);
oSettings.Angle.Unit.Value = Autodesk.Civil.AngleUnitType.Radian;
oSettings.Coordinate.Unit.Value = Autodesk.Civil.LinearUnitType.Foot;
oSettings.Distance.Unit.Value = Autodesk.Civil.LinearUnitType.Foot;
Settings classes: SettingsPipe, SettingsPipeNetwork, SettingsStructure (all inherit from SettingsAmbient).
Parts Lists
Parts lists contain families of pipe and structure types:
PartsListCollection partsListCol = doc.Styles.PartsListSet;
foreach (ObjectId plId in partsListCol)
{
PartsList partsList = ts.GetObject(plId, OpenMode.ForWrite) as PartsList;
ed.WriteMessage("Parts List: {0}\n", partsList.Name);
ObjectIdCollection pipeFamilies =
partsList.GetPartFamilyIdsByDomain(DomainType.Pipe);
foreach (ObjectId pfId in pipeFamilies)
{
PartFamily family = ts.GetObject(pfId, OpenMode.ForWrite) as PartFamily;
ed.WriteMessage(" Family: {0}\n", family.Name);
SizeFilterRecord sizeFilter = family.PartSizeFilter;
SizeFilterField material = sizeFilter.GetParamByContextAndIndex(
PartContextType.Material, 0);
}
}
Creating a Pipe Network
string networkName = "My Network";
ObjectId networkId = Network.Create(doc, ref networkName);
Network network = ts.GetObject(networkId, OpenMode.ForWrite) as Network;
network.ReferenceSurfaceId = surfaceId;
ObjectId partsListId = doc.Styles.PartsListSet["Standard"];
PartsList partsList = ts.GetObject(partsListId, OpenMode.ForWrite) as PartsList;
ObjectId pipeFamilyId = partsList["Concrete Pipe"];
PartFamily pipeFamily = ts.GetObject(pipeFamilyId, OpenMode.ForWrite) as PartFamily;
ObjectId pipeSize = pipeFamily[0];
ObjectId structFamilyId = partsList["Rectangular EndSection"];
PartFamily structFamily = ts.GetObject(structFamilyId, OpenMode.ForWrite) as PartFamily;
ObjectId structSize = structFamily[0];
Creating Pipes
LineSegment3d line = new LineSegment3d(
new Point3d(30, 9, 0), new Point3d(33, 7, 0));
ObjectId newPipeId = ObjectId.Null;
network.AddLinePipe(pipeFamilyId, pipeSize, line, ref newPipeId, false);
PipeCurveGeometry curveGeom = new PipeCurveGeometry(...);
ObjectId curvedPipeId = network.AddCurvePipe(pipeFamilyId, pipeSize, curveGeom, false);
Breaking and Moving Pipes
ObjectId newPipeId = ObjectId.Null;
network.BreakPipe(pipeIdToBreak, breakPoint, existingStructureId, ref newPipeId);
ObjectIdCollection partIds = new ObjectIdCollection();
partIds.Add(pipeId);
network.MoveParts(partIds, destinationNetworkId);
double minLength = 0;
ObjectIdCollection pathIds = Network.FindShortestNetworkPath(
startPartId, endPartId, ref minLength);
Creating Structures
Point3d location = new Point3d(30, 9, 0);
ObjectId newStructId = ObjectId.Null;
network.AddStructure(structFamilyId, structSize, location,
0 , ref newStructId, true);
Connecting Pipes and Structures
pipe.ConnectToStructure(ConnectorPositionType.Start, structureId, false);
pipe.ConnectToStructure(ConnectorPositionType.End, structureId, false);
structure.ConnectToPipe(pipeId, ConnectorPositionType.Start);
ObjectId newJunctionId = ObjectId.Null;
pipe1.ConnectToPipe(ConnectorPositionType.End, pipe2Id,
ConnectorPositionType.Start, structFamilyId, structSizeId,
ref newJunctionId, true , false );
pipe.Disconnect(ConnectorPositionType.Start);
structure.Disconnect(pipeId);
ObjectId startStruct = pipe.StartStructureId;
ObjectId endStruct = pipe.EndStructureId;
int count = structure.ConnectedPipesCount;
string[] pipeNames = structure.GetConnectedPipeNames();
bool flowsIn = structure.IsConnectedPipeFlowingIn(0);
bool flowsOut = structure.IsConnectedPipeFlowingOut(0);
Note: Connecting pipes directly creates a virtual Structure as the joint. Disconnect a pipe before connecting to a different structure.
Dynamic Part Properties
Pipe oPipe = ts.GetObject(pipeId, OpenMode.ForRead) as Pipe;
PartDataField[] dataFields = oPipe.PartData.GetAllDataFields();
foreach (PartDataField field in dataFields)
{
ed.WriteMessage("Name: {0}, Desc: {1}, Type: {2}, Value: {3}\n",
field.Name, field.Description, field.DataType, field.Value);
}
Pipe Styles
ObjectId pipeStyleId = doc.Styles.PipeStyles.Add("My Pipe Style");
PipeStyle style = ts.GetObject(pipeStyleId, OpenMode.ForWrite) as PipeStyle;
style.PlanOption.WallSizeType = PipeWallSizeType.UserDefinedWallSize;
style.PlanOption.WallSizeOptions = PipeUserDefinedType.UseDrawingScale;
style.PlanOption.InnerDiameter = 0.0021;
style.PlanOption.OuterDiameter = 0.0024;
style.PlanOption.EndSizeType = PipeEndSizeType.UserDefinedEndSize;
style.GetDisplayStylePlan(PipeDisplayStylePlanType.OutsideWalls).Color =
Color.FromRgb(255, 191, 0);
style.GetDisplayStylePlan(PipeDisplayStylePlanType.InsideWalls).Color =
Color.FromRgb(191, 0, 255);
style.GetHatchStylePlan().Pattern = "DOTS";
style.GetHatchStylePlan().HatchType = HatchType.PreDefined;
style.PlanOption.HatchOptions = PipeHatchType.HatchToInnerWalls;
pipe.StyleId = pipeStyleId;
Structure Styles
ObjectId structStyleId = doc.Styles.StructureStyles.Add("My Struct Style");
StructureStyle style = ts.GetObject(structStyleId, OpenMode.ForWrite) as StructureStyle;
style.GetDisplayStylePlan(StructureDisplayStylePlanType.Structure).Color =
Color.FromRgb(255, 191, 0);
style.PlanOption.SizeType = StructureSizeOptionsType.UseDrawingScale;
style.PlanOption.Size = 0.0035;
structure.StyleId = structStyleId;
Pipe and Structure Label Styles
var pipeLabelStyles = doc.Styles.LabelStyles.PipeLabelStyles;
var structLabelStyles = doc.Styles.LabelStyles.StructureLabelStyles;
Creating Labels with Specific Styles
Individual pipe and structure labels can be created with a specific label style, or the style can be changed after creation via Label.StyleId.
ObjectId labelId = PipeLabel.Create(pipeId, 0.5 , labelStyleId);
ObjectId sLabelId = StructureLabel.Create(structureId, labelStyleId, labelLocation);
ObjectId ppLabelId = PipeProfileLabel.Create(
profileViewPartId, profileViewId, 0.5 , labelStyleId);
ObjectId spLabelId = StructureProfileLabel.Create(
profileViewId, profileViewPartId, labelStyleId);
ObjectId secPLabelId = PipeSectionLabel.Create(
sectionViewId, pipeId, sectionPipeNetworkId, partIndex, labelStyleId);
ObjectId secSLabelId = StructureSectionLabel.Create(
sectionViewId, structureId, sectionPipeNetworkId, partIndex, labelStyleId);
Label label = ts.GetObject(labelId, OpenMode.ForWrite) as Label;
label.StyleId = newLabelStyleId;
ObjectIdCollection pipeLabels = pipe.GetPipeLabelIds();
ObjectIdCollection structLabels = structure.GetAvailableStructureLabelIds();
Pressure Network Labels
Pressure label classes have different signatures from gravity labels. Note argument order differences:
ObjectId pipeLabelId = PressurePipeLabel.Create(pipeId, 0.5 , pipeStyleId);
ObjectId fittingLabelId = PressureFittingLabel.Create(
fittingId, fittingStyleId, 0.5 , new Vector3d(1, 0, 0));
ObjectId appurtLabelId = PressureAppurtenanceLabel.Create(
appurtId, appurtStyleId, 0.5 , new Vector3d(1, 0, 0));
ObjectId pipeSectLabelId = PressurePipeSectionLabel.Create(
sectionViewId, pipeId, sectionNetworkId,
0 , 0.5, new Vector3d(1, 0, 0),
labelStyleId, (DimensionAnchorOptionType)0, 0.0);
ObjectId fittingSectLabelId = PressureFittingSectionLabel.Create(
sectionViewId, fittingId, sectionNetworkId,
0, 0.5, new Vector3d(1, 0, 0),
labelStyleId, (DimensionAnchorOptionType)0, 0.0);
ObjectId appurtSectLabelId = PressureAppurtenanceSectionLabel.Create(
sectionViewId, appurtId, sectionNetworkId,
0, 0.5, new Vector3d(1, 0, 0),
labelStyleId, (DimensionAnchorOptionType), );
Detecting Existing Pressure Labels
ObjectIdCollection existingPipe = PressurePipeLabel.GetAvailableLabelIds(pipeId);
ObjectIdCollection existingFit = PressureFittingLabel.GetAvailableLabelIds(fittingId);
ObjectIdCollection existingApp = PressureAppurtenanceLabel.GetAvailableLabelIds(appurtId);
ObjectIdCollection existingPipeSect = PressurePipeSectionLabel.GetAvailableLabelIds(
sectionViewId, pipeId, sectionNetworkId);
ObjectIdCollection existingFitSect = PressureFittingSectionLabel.GetAvailableLabelIds(
sectionViewId, fittingId, sectionNetworkId);
ObjectIdCollection existingAppSect = PressureAppurtenanceSectionLabel.GetAvailableLabelIds(
sectionViewId, appurtId, sectionNetworkId);
Profile View Part Discovery
Gravity and pressure networks use different APIs to discover which parts are drawn in a profile view:
Gravity (reflection fallback required)
PipeOverrideCollection pipeOverrides = profileView.PipeOverrides;
foreach (PipeOverride o in pipeOverrides)
{
ObjectId pipeId = o.PipeId;
Pipe pipe = tr.GetObject(pipeId, OpenMode.ForRead) as Pipe;
networkIds.Add(pipe.NetworkId);
}
StructureOverrideCollection structOverrides = profileView.StructureOverrides;
foreach (StructureOverride o in structOverrides)
{
ObjectId structId = GetObjectIdProperty(o, "StructId", "StructureId", "PartId", "FeatureId", "SourceId");
}
System.Collections.IEnumerable graphOverrides = profileView.GraphOverrides;
foreach (object o in graphOverrides)
{
ObjectId partId = GetObjectIdProperty(o, "PipeId", "StructId", "StructureId", "PartId", "FeatureId", "SourceId");
}
ObjectId GetObjectIdProperty(object source, params string[] names)
{
var type = source.GetType();
foreach (var name in names)
{
var prop = type.GetProperty(name);
if (prop?.PropertyType == typeof(ObjectId))
return (ObjectId)prop.GetValue(source, null);
}
return ObjectId.Null;
}
Pressure (clean API, no reflection needed)
ObjectIdCollection partsInGraph = profileView.GetPressureNetworkPartsInGraph();
foreach (ObjectId profilePartId in partsInGraph)
{
ProfileViewPressurePart profilePart =
tr.GetObject(profilePartId, OpenMode.ForRead, false, true) as ProfileViewPressurePart;
if (profilePart == null) continue;
Entity modelPart = tr.GetObject(profilePart.ModelPartId, OpenMode.ForRead) as Entity;
if (modelPart is PressurePipe pp)
networkIds.Add(pp.NetworkId);
else if (modelPart is PressureFitting pf)
networkIds.Add(pf.NetworkId);
else if (modelPart is PressureAppurtenance pa)
networkIds.Add(pa.NetworkId);
}
Interference Checks
Interference check styles control the display of intersections:
ObjectId intStyleId = doc.Styles.InterferenceStyles.Add("My Interference Style");
InterferenceStyle intStyle = ts.GetObject(intStyleId, OpenMode.ForWrite)
as InterferenceStyle;
intStyle.GetDisplayStylePlan(InterferenceDisplayStyleType.Symbol).Visible = true;
ObjectId markerStyleId = intStyle.MarkerStyle;
MarkerStyle marker = ts.GetObject(markerStyleId, OpenMode.ForWrite) as MarkerStyle;
marker.MarkerType = MarkerDisplayType.UseCustomMarker;
marker.CustomMarkerStyle = CustomMarkerType.CustomMarkerX;
marker.CustomMarkerSuperimposeStyle = CustomMarkerSuperimposeType.Circle;
marker.SizeType = MarkerSizeType.AbsoluteUnits;
marker.MarkerSize = 5.5;
Existing Interference objects can be read to get the two intersecting network IDs:
Interference interference = ts.GetObject(interferenceId, OpenMode.ForRead) as Interference;
ObjectId net1 = interference.Network1Id;
ObjectId net2 = interference.Network2Id;
Note: Creating/executing new interference checks programmatically is not exposed in the .NET API. Use the Civil 3D UI to run interference checks, then read the results via the Interference class.
Pressure Pipe Networks
Pressure networks use separate classes from the AeccPressurePipesMgd assembly:
ObjectId pressNetId = PressurePipeNetwork.Create(db, "My Pressure Network");
PressurePipeNetwork pressNet = ts.GetObject(pressNetId, OpenMode.ForWrite)
as PressurePipeNetwork;
pressNet.ReferenceSurfaceId = surfaceId;
pressNet.ReferenceAlignmentId = alignmentId;
pressNet.PartsListId = partsListId;
LineSegment3d line = new LineSegment3d(startPt, endPt);
ObjectId pipeId = pressNet.AddLinePipe(line, pressurePartSize);
ObjectId fittingId = pressNet.AddFitting(location, fittingPartSize);
ObjectId appurtId = pressNet.AddAppurtenance(location, appurtPartSize);
ObjectIdCollection pipeIds = pressNet.GetPipeIds();
ObjectIdCollection fittingIds = pressNet.GetFittingIds();
ObjectIdCollection appurtIds = pressNet.GetAppurtenanceIds();
PressurePipe pPipe = ts.GetObject(pipeId, OpenMode.ForWrite) as PressurePipe;
pPipe.StyleId = pipeStyleId;
PressureFitting fitting = ts.GetObject(fittingId, OpenMode.ForWrite) as PressureFitting;
fitting.StyleId = fittingStyleId;
PressureAppurtenance appurt = ts.GetObject(appurtId, OpenMode.ForWrite) as PressureAppurtenance;
appurt.StyleId = appurtStyleId;
pressNet.PipePlanLabelStyleId = pipeLabelStyleId;
pressNet.FittingPlanLabelStyleId = fittingLabelStyleId;
pressNet.AppurtenancePlanLabelStyleId = appurtLabelStyleId;
pressNet.PipeProfileLabelStyleId = pipeProfileLabelStyleId;
pressNet.FittingProfileLabelStyleId = fittingProfileLabelStyleId;
pressNet.AppurtenanceProfileLabelStyleId = appurtProfileLabelStyleId;
PressurePartSize newSize = ...;
pPipe.SwapPartSize(newSize);
The pressure pipe network API follows similar patterns to gravity networks but with separate style collections for pipes, fittings, and appurtenances (three separate style selections needed). Pressure parts use PressurePartSize instead of family/size ObjectId pairs.
Network Catalog Custom Properties
NetworkCatalogDef supports declaring custom part parameters:
NetworkCatalogDef.DeclareNewParameter(
"globalContext", "displayContext", "paramName", "paramDesc",
PartCatalogDataType.Integer, PartParamUsageType.Dynamic,
"defaultUnits", false , false );
NetworkCatalogDef.DeclarePartProperty("globalContext", DomainType.Pipe, PartType.Pipe);
Gotchas
ReferenceSurfaceId is used primarily for pipe rules (e.g., rim elevation relative to surface)
Network.Create takes the network name as ref string - Civil 3D may modify the name to avoid duplicates
- Individual pipe/structure labels CAN be created with a specific
labelStyleId and changed after creation via Label.StyleId
- Executing new interference checks is not exposed in .NET, but existing
Interference objects can be read
- Parts list items are identified by GUID -
PartFamily can only contain one domain (pipe OR structure)
- When creating a pipe style, add Try/Catch:
Add() throws if name exists, use Item() to get existing
- Connecting pipes directly creates virtual structure joints automatically
- Pressure networks use
PressurePartSize for part sizing instead of family/size ObjectId pairs
Pipe.StyleId has only a setter override on Pipe/Structure; the getter is inherited from Entity
Label Filtering (from this codebase)
The LabelFilterHelper in SharedTypes.cs maps parts list prefixes to label style prefixes:
- Gravity: pipes + structures (two style categories)
- Pressure: pipes + fittings + appurtenances (three style categories)
Related Skills
c3d-root-objects - Accessing networks through CivilDocument (GetPipeNetworkIds / GetPressurePipeNetworkIds)
c3d-label-styles - Pipe/pressure label style navigation and class hierarchy table
c3d-profiles - Profile views where pipe profile labels are placed
c3d-sample-lines - Sample line groups and section views where pipe section labels are placed
c3d-corridors - Corridor-pipe interactions
c3d-pressure-networks - Dedicated pressure network skill with detailed fitting, appurtenance, and pressure parts list coverage