|
| 1 | +#!meta |
| 2 | + |
| 3 | +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":".NET"},{"aliases":["C#","c#"],"languageName":"C#","name":"csharp"},{"aliases":["frontend"],"name":"vscode"}]}} |
| 4 | + |
| 5 | +#!csharp |
| 6 | + |
| 7 | +#r "nuget: Elements.MEP" |
| 8 | + |
| 9 | +#!markdown |
| 10 | + |
| 11 | +The routing basis for MEP systems is Elements.Flow.Tree. The tree is a geometric graph, with Flow.Nodes that are connected by Flow.Connections. The tree is simply meant to represent where the system paths are, where the paths merge/branch and what amount of flow is in each connection. You can optionally provide a diameter for the connections, to get a system that has realistic sizes. |
| 12 | + |
| 13 | +The leaves of the system are called Inlets (purely for historical reasons) and the trunk node is called the Outlet (again, for historical reasons). There is only one Outlet, but there can be many Inlets. |
| 14 | + |
| 15 | +The simplest tree is flat and just has some inlets and some outlets, lets make that now. |
| 16 | + |
| 17 | +#!csharp |
| 18 | + |
| 19 | +using Elements.Flow; |
| 20 | + |
| 21 | +var tree= new Tree(new[] {"Room-101"}); |
| 22 | + |
| 23 | +tree.SetOutletPosition((0,-1,0)); |
| 24 | + |
| 25 | +tree.AddInlet((-1,1,0 )); |
| 26 | +tree.AddInlet((1,5,0)); |
| 27 | + |
| 28 | +return tree; |
| 29 | + |
| 30 | +#!markdown |
| 31 | + |
| 32 | +Note that both inlets are connected directly to the outlet, which is not a very interesting system. Let's change this to make it so that the inlets join together before going to the outlet. We'll see some of our first ways in modifying and interacting with the `Tree`. |
| 33 | + |
| 34 | +### TODO Add a description of there being Nodes/Edges in the Tree |
| 35 | + |
| 36 | +#!csharp |
| 37 | + |
| 38 | +using Elements.Flow; |
| 39 | + |
| 40 | +var tree= new Tree(new[] {"Room-101"}); |
| 41 | + |
| 42 | +tree.SetOutletPosition((0,-1,0)); |
| 43 | + |
| 44 | +var inlet1 = tree.AddInlet((-1,1,0 )); |
| 45 | +var connection = tree.GetOutgoingConnection((inlet1)); |
| 46 | +var node = tree.SplitConnectionThroughPoint(connection, (0,1,0), out _); |
| 47 | + |
| 48 | +// when we add an inlet we can specify a node to connect via |
| 49 | +var inlet2 = tree.AddInlet((1,5,0), connectVia: node); |
| 50 | + |
| 51 | +return tree; |
| 52 | + |
| 53 | +#!markdown |
| 54 | + |
| 55 | +Note the small cube that has appeared. These cubes appear at every node where there is more than one incoming connection. They are places where the Tree branches. |
| 56 | + |
| 57 | +The Tree class is designed to let you modify the connections of the system while always maintaining internal connectivity. Let's clean up the connection from our second inlet so that it isn't at an angle. |
| 58 | + |
| 59 | +#!csharp |
| 60 | + |
| 61 | +var inlet2 = tree.Inlets[1]; |
| 62 | +var connection = tree.GetOutgoingConnection(inlet2); |
| 63 | +tree.NormalizeConnectionPath(connection, Vector3.ZAxis, Vector3.XAxis, 90, Tree.NormalizationType.End); |
| 64 | + |
| 65 | +return tree; |
| 66 | + |
| 67 | +#!markdown |
| 68 | + |
| 69 | +Here are some other common Tree manipulation methods: |
| 70 | + |
| 71 | +```csharp |
| 72 | +tree.ChamferAtNode(tree.InternalNodes.First(), 0.5, out _, out _); |
| 73 | +tree.ConnectVertically(tree.Connections.First(), 0.5); |
| 74 | +tree.MergeConnectionsAtPoint(tree.Connections.ToList(), (0,1,0)); |
| 75 | +tree.ShiftConnectionToNode(tree.Connections.First(), tree.Outlet); |
| 76 | +``` |
| 77 | + |
| 78 | +#!markdown |
| 79 | + |
| 80 | +Let's build a slightly more complex tree and give it some flow values. |
| 81 | + |
| 82 | +#!csharp |
| 83 | + |
| 84 | +using Elements.Flow; |
| 85 | + |
| 86 | +var tree= new Tree(new[] {"Room-101"}); |
| 87 | +tree.SetOutletPosition((0,-1,0)); |
| 88 | + |
| 89 | +var inlet1 = tree.AddInlet((-1,1,0), 1); |
| 90 | +var inlet2 = tree.AddInlet((-1,5,0), 2); |
| 91 | +var inlet3 = tree.AddInlet((1,2,0), 3); |
| 92 | + |
| 93 | +var connection1 = tree.GetOutgoingConnection(inlet1); |
| 94 | +var connection2 = tree.GetOutgoingConnection(inlet2); |
| 95 | +var node= tree.MergeConnectionsAtPoint(new List<Connection> {connection1, connection2}, (0,1,0)); |
| 96 | + |
| 97 | +// connection1 is a new connection after the previous change |
| 98 | +connection2 = tree.GetOutgoingConnection(inlet2); |
| 99 | +var connection3 = tree.GetOutgoingConnection(inlet3); |
| 100 | +tree.ShiftConnectionToNode(connection3, node); |
| 101 | +tree.MergeConnectionsAtPoint(new List<Connection> {connection2, connection3}, (0,2,0)); |
| 102 | + |
| 103 | +connection2 = tree.GetOutgoingConnection(inlet2); |
| 104 | +tree.NormalizeConnectionPath(connection2, Vector3.ZAxis, Vector3.XAxis, 90, Tree.NormalizationType.End); |
| 105 | + |
| 106 | +return tree; |
| 107 | + |
| 108 | +#!markdown |
| 109 | + |
| 110 | +Now, you have a tree with some flow values and some branching connections. Let's find the flow at the connections. |
| 111 | + |
| 112 | +#!csharp |
| 113 | + |
| 114 | +tree.UpdateSections(); |
| 115 | + |
| 116 | +var connection3 = tree.GetOutgoingConnection(inlet3); |
| 117 | +Console.WriteLine($"Flow from inlet1: {tree.GetFlowOfConnection(connection3)}"); |
| 118 | + |
| 119 | +var trunk = tree.GetIncomingConnections(tree.Outlet).First(); |
| 120 | +Console.WriteLine($"Flow from outlet: {tree.GetFlowOfConnection(trunk)}"); |
| 121 | + |
| 122 | +#!markdown |
| 123 | + |
| 124 | +The `UpdateSections()` method is used to update the flow values in the connections. It also updates the "Sections" property of the tree, where each section is a list of connections that are connected to each other but without any branching. Let's take a look at what the sections look like. |
| 125 | + |
| 126 | +#!csharp |
| 127 | + |
| 128 | +var sections = tree.GetSections(); |
| 129 | +Console.WriteLine($"Number of connections: {tree.Connections.Count()}"); |
| 130 | +Console.WriteLine($"Number of sections: {sections.Count()}"); |
| 131 | + |
| 132 | +return sections; |
| 133 | + |
| 134 | +#!markdown |
| 135 | + |
| 136 | +Now lets create fittings for this tree. For that we'll use the `Elements.Fittings` namespace, and we can create fittings with just one one line. |
| 137 | + |
| 138 | +#!csharp |
| 139 | + |
| 140 | +using Elements.Fittings; |
| 141 | + |
| 142 | +var fittings = new FittingTreeRouting(tree).BuildFittingTree(out var errors); |
| 143 | + |
| 144 | +return fittings; |
| 145 | + |
| 146 | +#!markdown |
| 147 | + |
| 148 | +These fitting were created using the default FittingTreeRouting, which you can modify to get different results if we want. |
| 149 | + |
| 150 | +#!csharp |
| 151 | + |
| 152 | +var routing = new FittingTreeRouting(tree); |
| 153 | +routing.PipeMaterial = new Material("Pipe", Colors.Aqua, 0.1, 0.1); |
| 154 | +var fittings = routing.BuildFittingTree(out var error);// FittingTree.FromFlowNetwork(tree, out var errors, routing); |
| 155 | + |
| 156 | +return fittings; |
| 157 | + |
| 158 | +#!markdown |
| 159 | + |
| 160 | +Besides just color, you can make a custom FittingTreeRouting that implements your own logic for creating fittings. For example, imagine you only have one size of "Terminal" and "Elbow" fittings, and you want to create your Fitting tree using only that one size. You do this by inheriting from the FittingTreeRouting class and overriding the `BranchPipe` and `TerminatePipe` methods. |
| 161 | + |
| 162 | +#!csharp |
| 163 | + |
| 164 | +public class OneSizeToRuleThemAll: FittingTreeRouting |
| 165 | +{ |
| 166 | + public OneSizeToRuleThemAll(Tree tree) : base(tree) |
| 167 | + { |
| 168 | + } |
| 169 | + |
| 170 | + public override Fitting ChangeDirection(Connection incoming, Connection outgoing) |
| 171 | + { |
| 172 | + var diameter = 0.1; |
| 173 | + var elbow = CreateElbow(diameter, incoming.End.Position, incoming.Direction().Negate(), outgoing.Direction()); |
| 174 | + return elbow; |
| 175 | + } |
| 176 | + |
| 177 | + public override Fitting TerminatePipe(Connection incoming, Connection outgoing, out Node[] absorbed) { |
| 178 | + var diameter = 0.1; |
| 179 | + absorbed = new Node[0]; |
| 180 | + if (outgoing != null) |
| 181 | + { |
| 182 | + var terminal = new Terminal(outgoing.Start.Position, outgoing.Direction(), 0.03, diameter, DefaultFittingMaterial); |
| 183 | + return terminal; |
| 184 | + } |
| 185 | + else if (incoming != null) |
| 186 | + { |
| 187 | + var terminal = new Terminal(incoming.End.Position, incoming.Direction().Negate(), 0.03, diameter, DefaultFittingMaterial); |
| 188 | + return terminal; |
| 189 | + } |
| 190 | + else |
| 191 | + { |
| 192 | + throw new ArgumentNullException("Both connections to terminate were null"); |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | +public override IReducer ReduceOrJoin(StraightSegment pipe, bool invert, double newDiameter, double additionalDistance = 0){ |
| 197 | + var reducer = base.ReduceOrJoin(pipe, invert, newDiameter, additionalDistance); |
| 198 | + (reducer as Reducer).Material = new Material("Reducer", Colors.Coral, newDiameter, newDiameter); |
| 199 | + return reducer; |
| 200 | +} |
| 201 | +} |
| 202 | + |
| 203 | +var routing = new OneSizeToRuleThemAll(tree); |
| 204 | +routing.PipeMaterial = new Material("Pipe", Colors.Aqua, 0.1, 0.1); |
| 205 | +var fittings = routing.BuildFittingTree(out var errors);// FittingTree.FromFlowNetwork(tree, out var errors, routing); |
| 206 | +Console.WriteLine($"Total number of reducers: {fittings.FittingsOfType<Reducer>().Count()}"); |
| 207 | + |
| 208 | +return fittings; |
| 209 | + |
| 210 | +#!markdown |
| 211 | + |
| 212 | +Cool! Now you have 100mm elbows and terminals. Notice that reducers were automatically added wherever necessary. The other methods you might be interested in modifying are: |
| 213 | + |
| 214 | +```csharp |
| 215 | +Fitting BranchPipe(Connection incoming1, Connection incoming2, Connection outgoing) // for single branch nodes. |
| 216 | +Fitting ManifoldPipe(IEnumerable<Connection> incoming, Connection outgoing) // for multi-branch nodes. |
| 217 | +Fitting ChangePipe(Connection incoming, Connection outgoing) // for nodes with a single incoming and outgoing connection that are aligned. |
| 218 | +``` |
0 commit comments