1717using Autodesk . Revit . DB . Plumbing ;
1818using Autodesk . Revit . UI ;
1919using Autodesk . Revit . UI . Selection ;
20+ using StructuralType = Autodesk . Revit . DB . Structure . StructuralType ;
2021#endregion // Namespaces
2122
2223namespace BuildingCoder
@@ -32,6 +33,13 @@ class CmdRollingOffset : IExternalCommand
3233 /// </summary>
3334 static bool _place_model_line = false ;
3435
36+ /// <summary>
37+ /// Place the two 45 degree fittings and connect
38+ /// them instead of explicitly placing the
39+ /// rolling offset pipe segment.
40+ /// </summary>
41+ static bool _place_fittings = true ;
42+
3543 /// <summary>
3644 /// Switch between the new static Pipe.Create
3745 /// method and the obsolete
@@ -48,6 +56,10 @@ const string _prompt
4856 + "this command, or post-select them when "
4957 + "prompted." ;
5058
59+ const BuiltInParameter bipDiameter
60+ = BuiltInParameter . RBS_PIPE_DIAMETER_PARAM ;
61+
62+
5163 /// <summary>
5264 /// Allow selection of curve elements only.
5365 /// </summary>
@@ -203,8 +215,8 @@ public Result Execute(
203215 return Result . Failed ;
204216 }
205217
206- // Select the two pipe endpoints that are
207- // farthest apart.
218+ // Select the two pipe endpoints
219+ // that are farthest apart.
208220
209221 XYZ p0 = p00 . DistanceTo ( p10 ) > p01 . DistanceTo ( p10 )
210222 ? p00
@@ -224,7 +236,16 @@ public Result Execute(
224236 return Result . Failed ;
225237 }
226238
239+ // Normal vector of the plane defined by the
240+ // two parallel and offset pipes, which is
241+ // the plane hosting the rolling offset
242+
227243 XYZ z = v . CrossProduct ( v1 ) ;
244+
245+ // Vector perpendicular to v0 and v0 and
246+ // z, i.e. vector pointing from the first pipe
247+ // to the second in the cross sectional view.
248+
228249 XYZ w = z . CrossProduct ( v1 ) . Normalize ( ) ;
229250
230251 // Offset distance perpendicular to pipe direction
@@ -273,7 +294,30 @@ double remainingPipeLength
273294
274295 using ( Transaction tx = new Transaction ( doc ) )
275296 {
276- Pipe pipe = null ;
297+ // Determine pipe diameter for creating
298+ // matching pipes and fittings
299+
300+ Pipe pipe = pipes [ 0 ] ;
301+
302+ double diameter = pipe
303+ . get_Parameter ( bipDiameter ) // "Diameter"
304+ . AsDouble ( ) ;
305+
306+ // Pipe type for calls to doc.Create.NewPipe
307+
308+ PipeType pipe_type_standard
309+ = new FilteredElementCollector ( doc )
310+ . OfClass ( typeof ( PipeType ) )
311+ . Cast < PipeType > ( )
312+ . Where < PipeType > ( e
313+ => e . Name . Equals ( "Standard" ) )
314+ . FirstOrDefault < PipeType > ( ) ;
315+
316+ Debug . Assert (
317+ pipe_type_standard . Id . IntegerValue . Equals (
318+ pipe . PipeType . Id . IntegerValue ) ,
319+ "expected all pipes in this simple "
320+ + "model to use the same pipe type" ) ;
277321
278322 tx . Start ( "Rolling Offset" ) ;
279323
@@ -294,11 +338,126 @@ double remainingPipeLength
294338 Line line = Line . CreateBound ( q0 , q1 ) ;
295339
296340 creator . CreateModelCurve ( line ) ;
341+
342+ pipe = null ;
297343 }
298- else
344+ else if ( _place_fittings )
299345 {
300- pipe = pipes [ 0 ] ;
346+ // Set active work plane to the rolling
347+ // offset plane... removed again, since
348+ // this has no effect at all on the
349+ // fitting placement or rotation.
350+ //
351+ //Plane plane = new Plane( z, q0 );
352+ //
353+ //SketchPlane sp = SketchPlane.Create(
354+ // doc, plane );
355+ //
356+ //uidoc.ActiveView.SketchPlane = sp;
357+ //uidoc.ActiveView.ShowActiveWorkPlane();
358+
359+ FamilySymbol symbol
360+ = new FilteredElementCollector ( doc )
361+ . OfClass ( typeof ( FamilySymbol ) )
362+ . OfCategory ( BuiltInCategory . OST_PipeFitting )
363+ . Cast < FamilySymbol > ( )
364+ . Where < FamilySymbol > ( e
365+ => e . Family . Name . Contains ( "Elbow - Generic" ) )
366+ . FirstOrDefault < FamilySymbol > ( ) ;
367+
368+ // Set up first 45 degree elbow fitting
369+
370+ FamilyInstance fitting0 = doc . Create
371+ . NewFamilyInstance ( q0 , symbol ,
372+ StructuralType . NonStructural ) ;
373+
374+ fitting0 . get_Parameter ( "Angle" ) . Set (
375+ 45.0 * Math . PI / 180.0 ) ;
376+
377+ //fitting0.get_Parameter( bipDiameter ) // does not exist
378+ // .Set( diameter );
379+
380+ fitting0 . get_Parameter ( "Nominal Radius" )
381+ . Set ( 0.5 * diameter ) ;
382+
383+ Line axis = Line . CreateBound ( p0 , q0 ) ;
384+ angle = z . AngleTo ( XYZ . BasisZ ) ;
385+
386+ ElementTransformUtils . RotateElement (
387+ doc , fitting0 . Id , axis , Math . PI - angle ) ;
388+
389+ Connector con0 = Util . GetConnectorClosestTo (
390+ fitting0 , p0 ) ;
391+
392+ // Trim or extend existing pipe
393+
394+ ( pipes [ 0 ] . Location as LocationCurve ) . Curve
395+ = Line . CreateBound ( p0 , con0 . Origin ) ;
396+
397+ // Connect pipe to fitting
398+
399+ Util . Connect ( con0 . Origin , pipe , fitting0 ) ;
400+
401+ // Set up second 45 degree elbow fitting
402+
403+ FamilyInstance fitting1 = doc . Create
404+ . NewFamilyInstance ( q1 , symbol ,
405+ StructuralType . NonStructural ) ;
406+
407+ fitting1 . get_Parameter ( "Angle" ) . Set (
408+ 45.0 * Math . PI / 180.0 ) ;
409+
410+ fitting1 . get_Parameter ( "Nominal Radius" )
411+ . Set ( 0.5 * diameter ) ;
412+
413+ axis = Line . CreateBound (
414+ q1 , q1 + XYZ . BasisZ ) ;
415+
416+ ElementTransformUtils . RotateElement (
417+ doc , fitting1 . Id , axis , Math . PI ) ;
301418
419+ axis = Line . CreateBound ( q1 , p1 ) ;
420+
421+ ElementTransformUtils . RotateElement (
422+ doc , fitting1 . Id , axis , Math . PI - angle ) ;
423+
424+ Connector con1 = Util . GetConnectorClosestTo (
425+ fitting1 , p1 ) ;
426+
427+ ( pipes [ 1 ] . Location as LocationCurve ) . Curve
428+ = Line . CreateBound ( con1 . Origin , p1 ) ;
429+
430+ Util . Connect ( con1 . Origin , fitting1 , pipes [ 1 ] ) ;
431+
432+ con0 = Util . GetConnectorClosestTo (
433+ fitting0 , pm ) ;
434+
435+ con1 = Util . GetConnectorClosestTo (
436+ fitting1 , pm ) ;
437+
438+ // Connecting one fitting to the other does
439+ // not insert a pipe in between. If the
440+ // system is edited later, however, the two
441+ // fittings snap together.
442+ //
443+ //con0.ConnectTo( con1 );
444+
445+ // Create rolling offset pipe segment
446+
447+ pipe = doc . Create . NewPipe ( con0 . Origin ,
448+ con1 . Origin , pipe_type_standard ) ;
449+
450+ pipe . get_Parameter ( bipDiameter )
451+ . Set ( diameter ) ;
452+
453+ // Connect rolling offset pipe segment
454+ // with elbow fittings at each end
455+
456+ Util . Connect ( con0 . Origin , fitting0 , pipe ) ;
457+ Util . Connect ( con1 . Origin , pipe , fitting1 ) ;
458+ }
459+ else
460+ {
302461 if ( _use_static_pipe_create )
303462 {
304463 ElementId idSystem = pipe . MEPSystem . Id ; // invalid
@@ -321,42 +480,20 @@ double remainingPipeLength
321480 }
322481 else
323482 {
324- BuiltInParameter bip
325- = BuiltInParameter . RBS_PIPE_DIAMETER_PARAM ;
326-
327- double diameter = pipe
328- . get_Parameter ( bip ) // "Diameter"
329- . AsDouble ( ) ;
330-
331- PipeType pipe_type_standard
332- = new FilteredElementCollector ( doc )
333- . OfClass ( typeof ( PipeType ) )
334- . Cast < PipeType > ( )
335- . Where < PipeType > ( e
336- => e . Name . Equals ( "Standard" ) )
337- . FirstOrDefault < PipeType > ( ) ;
338-
339- Debug . Assert (
340- pipe_type_standard . Id . IntegerValue . Equals (
341- pipe . PipeType . Id . IntegerValue ) ,
342- "expected all pipes in this simple "
343- + "model to use the same pipe type" ) ;
344-
345483 pipe = doc . Create . NewPipe ( q0 , q1 ,
346484 pipe_type_standard ) ;
347485
348- pipe . get_Parameter ( bip )
486+ pipe . get_Parameter ( bipDiameter )
349487 . Set ( diameter ) ;
350488 }
351- }
352-
353- if ( null != pipe )
354- {
355- // Connect rolling offset pipe segment
356- // with its neighbours
489+ if ( null != pipe )
490+ {
491+ // Connect rolling offset pipe segment
492+ // with its neighbours
357493
358- Util . Connect ( q0 , pipes [ 0 ] , pipe ) ;
359- Util . Connect ( q1 , pipe , pipes [ 1 ] ) ;
494+ Util . Connect ( q0 , pipes [ 0 ] , pipe ) ;
495+ Util . Connect ( q1 , pipe , pipes [ 1 ] ) ;
496+ }
360497 }
361498
362499 tx . Commit ( ) ;
0 commit comments