Author Topic: I'm trying to identify pipe geometry that can be in any orientation  (Read 1959 times)

0 Members and 1 Guest are viewing this topic.

Michael2

  • Mosquito
  • Posts: 6
Howdy folks!

I'm trying to find pipe segment sizes (diameters) and length in AutoCAD Mecanical 2018 files. In all cases, the pipe segments will be Solid3D types, with hollow centers. I quickly sketched in some code to do this, and it seems to work, but I'm not very bright so it will only work if the pipe is oriented along the Z axis. Easy-peasy. I could get it to work in other orthagonal views with a bunch of If/Thens, but that won't work for all cases.
My problem is that I need it to work with the pipe segment in any orientation.
These pipes will be mixed in with many other non-pipe solids, so the object has to be identified as a pipe, then it's diameter must be determined.

Note that I cannot rely on the pipe ends being squared, so I don't think I can iterate through brep.faces looking for circular faces of a certain diameter or do things like that.

I've attached a dwg file that has 2 pipe segments in it. A simple one that is oriented along the Z axis, and a worst-case example, where I've rotated the pipe segment slightly in all 3 axiis, and then I've cut the ends.

Here is the initial code that I tested with (it works with a Z-aligned pipe segment):
Code: [Select]
       
        Dim sf = New SelectionFilter(New TypedValue() {New TypedValue(CInt(DxfCode.Start), "3DSOLID")})
        Dim selResult As PromptSelectionResult = acEd.SelectAll(sf)
 
        Dim PipeODs As New Dictionary(Of String, Double)    ' (OD, Nom)
        PipeODs.Add(CDbl(0.84).ToString, 0.5)
        PipeODs.Add(CDbl(1).ToString, 0.75)
        PipeODs.Add(CDbl(1.3125).ToString, 1)
        PipeODs.Add(CDbl(1.875).ToString, 1.5)
        PipeODs.Add(CDbl(2.375).ToString, 2)
        PipeODs.Add(CDbl(3.5).ToString, 3)
        PipeODs.Add(CDbl(4.5).ToString, 4)
        PipeODs.Add(CDbl(6.625).ToString, 6)
        PipeODs.Add(CDbl(8.625).ToString, 8)
        PipeODs.Add(CDbl(10.75).ToString, 10)

Using acDoc.LockDocument()
            Using acTr As Transaction = acDoc.TransactionManager.StartTransaction
                Dim acBlkTbl As BlockTable = acTr.GetObject(acDB.BlockTableId, OpenMode.ForRead)
                Dim acBlkTblRec As BlockTableRecord = acTr.GetObject(acBlkTbl(BlockTableRecord.ModelSpace), OpenMode.ForWrite)

                For Each Obj As SelectedObject In selResult.Value   ' loop thru each 3dsolid
                    Dim eee As Entity = acTr.GetObject(Obj.ObjectId, OpenMode.ForRead)
                    Dim objWork As Solid3d = acTr.GetObject(Obj.ObjectId, OpenMode.ForWrite) '.Clone
                    If objWork.MassProperties.Volume = 0 Then
                        Continue For
                    End If

                    Dim brep As New BoundaryRepresentation.Brep(objWork)

                    Dim EXT As BoundBlock3d = brep.BoundBlock
                    If EXT.Direction1.Length = EXT.Direction2.Length Then
                        If PipeODs.ContainsKey(EXT.Direction1.Length.ToString) Then
                            Dim pipeod As Double = EXT.Direction1.Length
                            Dim pipelength As Double = EXT.Direction3.Length
                            'pa.Pipes.Add(New Pipe(pipeod, pipelength, Obj.ObjectId))
                            objWork.Color = Autodesk.AutoCAD.Colors.Color.FromColor(System.Drawing.Color.White)
                        End If
                    End If

                Next

            End Using
        End Using

The "Dim EXT As BoundBlock3d = brep.BoundBlock" and the following If/Then are where (I assume) the magic needs to happen. I just do not understand rotation matrices and coordinate system matrices so it is beyond me.

I hope this is enough info. If anyone can help me out, it would really be appreciated.
Thanks!!

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: I'm trying to identify pipe geometry that can be in any orientation
« Reply #1 on: April 07, 2020, 11:51:40 AM »
Hi,

This seems to work:
Code - C#: [Select]
  1.         public static bool TryGetPipe(Solid3d solid, out Pipe pipe)
  2.         {
  3.             pipe = new Pipe();
  4.             using (var brep = new Brep(solid))
  5.             {
  6.                 // must have 4 edges
  7.                 var edges = brep.Edges;
  8.                 if (edges.Count() != 4)
  9.                     return false;
  10.  
  11.                 // all eges must be circular or elliptical
  12.                 var curves = edges.Select(e => ((ExternalCurve3d)e.Curve).NativeCurve);
  13.                 if (!curves.All(c => c is CircularArc3d || c is EllipticalArc3d))
  14.                     return false;
  15.  
  16.                 // must have 2 distinct centers
  17.                 var centers = curves
  18.                     .Select(c => c is CircularArc3d ? ((CircularArc3d)c).Center : ((EllipticalArc3d)c).Center)
  19.                     .Distinct()
  20.                     .ToArray();
  21.                 if (centers.Length != 2)
  22.                     return false;
  23.  
  24.                 // must have 2 distinct radii
  25.                 var radii = curves
  26.                     .Select(c => c is CircularArc3d ? ((CircularArc3d)c).Radius : ((EllipticalArc3d)c).MinorRadius)
  27.                     .Distinct(new DoubleComparer())
  28.                     .ToArray();
  29.                 if (radii.Length != 2)
  30.                     return false;
  31.  
  32.                 pipe = new Pipe()
  33.                 {
  34.                     Diameter = Math.Max(radii[0], radii[1]) * 2.0,
  35.                     Direction = centers[0].GetVectorTo(centers[1]).GetNormal(),
  36.                     Length = centers[0].DistanceTo(centers[1]),
  37.                     Thickness = Math.Abs(radii[0] - radii[1])
  38.                 };
  39.                 return true;
  40.             }
  41.         }
  42.  
  43.         struct Pipe
  44.         {
  45.             public double Diameter { get; set; }
  46.             public Vector3d Direction { get; set; }
  47.             public double Length { get; set; }
  48.             public double Thickness { get; set; }
  49.         }
  50.  
  51.         class DoubleComparer : IEqualityComparer<double>
  52.         {
  53.             public bool Equals(double x, double y) =>
  54.                 Math.Round(x, 9) == Math.Round(x, 9);
  55.             public int GetHashCode(double d) => Math.Round(d, 9).GetHashCode();
  56.         }

A testing command:
Code - C#: [Select]
  1.         [CommandMethod("TEST")]
  2.         public static void Test()
  3.         {
  4.             var doc = Application.DocumentManager.MdiActiveDocument;
  5.             var db = doc.Database;
  6.             var ed = doc.Editor;
  7.             var options = new PromptEntityOptions("\nSelect a solid 3d: ");
  8.             options.SetRejectMessage("\nSelected object is not a solid3d.");
  9.             options.AddAllowedClass(typeof(Solid3d), true);
  10.             var result = ed.GetEntity(options);
  11.             if (result.Status != PromptStatus.OK)
  12.                 return;
  13.             using (var tr = db.TransactionManager.StartTransaction())
  14.             {
  15.                 var solid = (Solid3d)tr.GetObject(result.ObjectId, OpenMode.ForRead);
  16.                 if (TryGetPipe(solid, out Pipe pipe))
  17.                 {
  18.                     ed.WriteMessage(
  19.                         $"\nLength: {pipe.Length}" +
  20.                         $"\nDiameter: {pipe.Diameter}" +
  21.                         $"\nThickness: {pipe.Thickness}" +
  22.                         $"\nDirection: {pipe.Direction}");
  23.                 }
  24.                 tr.Commit();
  25.             }
  26.         }
Speaking English as a French Frog

Michael2

  • Mosquito
  • Posts: 6
Re: I'm trying to identify pipe geometry that can be in any orientation
« Reply #2 on: April 08, 2020, 04:09:40 PM »
Thank you so much gile, for your reply. I apologize for taking so long to get back to you.
Your code works for the most part, however I suspected that the "length" was not going to be calculated the way I need so I made a 3rd example pipe (attached).
I was expecting to try to figure out how to get the maximum distance between the 2 curves (because that would be the length of pipe needed in to cut the pipe) rather than the distance from center to center.
But when I ran the function on this example, it returned 4 elements in centers, so it is not being seen as Pipe. Strange thing is that 2 of the elements are identical to each other and the other 2 elements are identical to each other... the array does not appear to be distinct.
Is it possibly a rounding issue?


gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: I'm trying to identify pipe geometry that can be in any orientation
« Reply #3 on: April 08, 2020, 05:07:32 PM »
Try like this (you cange the values in the DoubleComparer and PointComparer to suit your needs)

Code - C#: [Select]
  1.         static bool TryGetPipe(Solid3d solid, out Pipe pipe)
  2.         {
  3.             pipe = new Pipe();
  4.             using (var brep = new Brep(solid))
  5.             {
  6.                 // must have 4 edges
  7.                 var edges = brep.Edges;
  8.                 if (edges.Count() != 4)
  9.                     return false;
  10.  
  11.                 // all eges must be circular or elliptical
  12.                 var externalCurves = edges.Select(e => (ExternalCurve3d)e.Curve);
  13.                 if (!externalCurves.All(c => c.IsCircularArc || c.IsEllipticalArc))
  14.                     return false;
  15.                 var nativeCurves = externalCurves.Select(c => c.NativeCurve).ToArray();
  16.  
  17.                 // must have 2 distinct centers
  18.                 var pointComparer = new PointComparer();
  19.                 var centers = nativeCurves
  20.                     .Select(c => c is CircularArc3d ? ((CircularArc3d)c).Center : ((EllipticalArc3d)c).Center)
  21.                     .Distinct(pointComparer)
  22.                     .ToArray();
  23.                 if (centers.Length != 2)
  24.                     return false;
  25.                 double length = centers[0].DistanceTo(centers[1]);
  26.  
  27.                 // must have 2 distinct radii
  28.                 var doubleComprer = new DoubleComparer();
  29.                 var radii = nativeCurves
  30.                     .Select(c => c is CircularArc3d ? ((CircularArc3d)c).Radius : ((EllipticalArc3d)c).MinorRadius)
  31.                     .Distinct(doubleComprer)
  32.                     .ToArray();
  33.                 if (radii.Length != 2)
  34.                     return false;
  35.                 double radius = Math.Max(radii[0], radii[1]);
  36.  
  37.                 foreach (var group in nativeCurves
  38.                                      .OfType<EllipticalArc3d>()
  39.                                      .GroupBy(e => e.Center, pointComparer))
  40.                 {
  41.                     double majRad = group.Max(e => e.MajorRadius);
  42.                     length += Math.Sqrt(majRad * majRad - radius * radius);
  43.                 }
  44.  
  45.                 pipe = new Pipe()
  46.                 {
  47.                     Diameter = radius * 2.0,
  48.                     Direction = centers[0].GetVectorTo(centers[1]).GetNormal(),
  49.                     Length = length,
  50.                     Thickness = Math.Abs(radii[0] - radii[1])
  51.                 };
  52.                 return true;
  53.             }
  54.         }
  55.  
  56.         struct Pipe
  57.         {
  58.             public double Diameter { get; set; }
  59.             public Vector3d Direction { get; set; }
  60.             public double Length { get; set; }
  61.             public double Thickness { get; set; }
  62.         }
  63.  
  64.         class DoubleComparer : IEqualityComparer<double>
  65.         {
  66.             public bool Equals(double x, double y) =>
  67.                 Math.Round(x, 9) == Math.Round(x, 9);
  68.             public int GetHashCode(double d) => Math.Round(d, 9).GetHashCode();
  69.         }
  70.  
  71.         class PointComparer : IEqualityComparer<Point3d>
  72.         {
  73.             public bool Equals(Point3d x, Point3d y) =>
  74.                x.IsEqualTo(y, new Tolerance(1e-10, 1e-9));
  75.             public int GetHashCode(Point3d p) =>
  76.                 new Point3d(Math.Round(p.X, 9), Math.Round(p.Y, 9), Math.Round(p.Z, 9)).GetHashCode();
  77.         }
Speaking English as a French Frog

MickD

  • King Gator
  • Posts: 3619
  • (x-in)->[process]->(y-out) ... simples!
Re: I'm trying to identify pipe geometry that can be in any orientation
« Reply #4 on: April 08, 2020, 07:03:03 PM »
If you can work out the centre line of the pipe you can rotate it to WCS, get bounding box length  in centre line direction and rotate back. This will give you the pipe length regardles of cuts on each end.

I've done this before and if you have a chance when creating the solids you can add XData (World Points from memory) as direction vectors that get updated when the solid is moved/rotated etc. This gives the solid it's own ECS and getting the length is then easy for any solid you create.

"Short cuts make long delays,' argued Pippin.”
J.R.R. Tolkien

Michael2

  • Mosquito
  • Posts: 6
Re: I'm trying to identify pipe geometry that can be in any orientation
« Reply #5 on: April 09, 2020, 02:10:40 PM »
Thank you so much gile. That seems to work.

And thank you MickD. Unfortunately I'm reading from a bunch of legacy dwgs so inserting xdata isn't an option. But I will try re-orienting the pipes to world if I find any issues with this new system.

Both please stay safe.