Find highest and lowest face of Brep - C#

Hello,

I’m using C# to try to find the lowest and highest face of a given brep, as measured by their centroid z value.

@DavidRutten posted this as a scripted VB component for GH:

  Private Sub RunScript(ByVal B As Brep, ByRef lowest As Object, ByRef highest As Object) 
    'Validate input
    If (B Is Nothing) Then Return

    'We'll iterate over all the faces and simultaneously remember the lowest and highest so far.
    Dim lowPoint As Double = Double.MaxValue
    Dim highPoint As Double = Double.MinValue

    For Each face As BrepFace In B.Faces
      'Convert the Face to a new Brep.
      Dim faceBrep As Brep = face.DuplicateFace(True)

      'Compute the Area Mass Properties of the brep
      Dim ap As AreaMassProperties = AreaMassProperties.Compute(faceBrep)

      'If the Area centroid is a new low, assign the brep to the lowest output.
      If (ap.Centroid.Z < lowPoint) Then
        lowPoint = ap.Centroid.Z
        lowest = faceBrep
      End If

      'If the Area centroid is a new high, assign the brep to the highest output.
      If (ap.Centroid.Z > highPoint) Then
        highPoint = ap.Centroid.Z
        highest = faceBrep
      End If
    Next
  End Sub 

I’ve tried to replicate a command in C#, but only got so far. I’m looking for help to complete the following code. I’m certain I’m not using the if/else statements correctly (just started learning c# 3 weeks ago):

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            ObjRef obj_ref;
            var rc = RhinoGet.GetOneObject("Select brep", false, ObjectType.Brep, out obj_ref);
            if (rc != Result.Success)
                return rc;
            var brep = obj_ref.Brep();

            //double lowpoint = double.MaxValue;
            //double highpoint = double.MinValue;

            foreach (var face in brep.Faces)
            {
                var faceBrep = face.DuplicateFace(true);

                //if (ap.Centroid.Z < lowPoint)
                    //lowPoint = ap.Centroid.Z
                    //lowest = faceBrep

                //else (ap.Centroid.Z > highPoint) 
                    //highPoint = ap.Centroid.Z
                    //highest = faceBrep


                Point3d ap = AreaMassProperties.Compute(faceBrep).Centroid;

                TextDot textdot = new TextDot("hello", ap);

                doc.Objects.AddTextDot(textdot);
                               
            }

            return Result.Success;
            
        }

for the lowest:

var lst = brep.Faces.OrderBy(f => AreaMassProperties.Compute(f).Centroid.Z).ToList();

and for the highest:

var lst = brep.Faces.OrderByDescending(f => AreaMassProperties.Compute(f).Centroid.Z).ToList();

its always item[0] in the list. typos possible, and you need to include linq in your using statements.

BrepFace topFace = brep.Faces.OrderByDescending(f => AreaMassProperties.Compute(f).Centroid.Z).ToList()[0];

does also work and makes it a one-line operation.

1 Like

Thank you!!

Here is where I’m at now. I’d like to label the top faces in the order that they’ve been selected. To do this I don’t think I can use a foreach loop, can I?

And are you able to use the .SetUserString method on BrepFaces?

protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            const ObjectType geometryFilter = ObjectType.Brep;

            GetObject go = new GetObject();

            go.GetMultiple(1, 0);
            go.GeometryFilter = geometryFilter;

            //I'D LIKE TO HAVE THE TOP FACES LABELED WITH THEIR ORDER OF SELECTION, SO NEED TO USE A 'PROPER' ITERATOR INSTEAD OF foreach, CORRECT?

            //for (var i = 0; i < --HOW TO MAKE A LIST FROM GetObject??-- .Count; i++)

            foreach (var i in go.Objects())
            {
                ObjectAttributes attributes = new ObjectAttributes();
                attributes.ObjectColor = Color.CadetBlue;

                var brep = i.Brep();

                var rhino_object = i.Object();

                var current_layer_index = doc.Layers.CurrentLayerIndex;
                rhino_object.Attributes.LayerIndex = current_layer_index;

                rhino_object.Attributes.Name = "CLT";

                var defaultkey = "key";
                var defaultval = "value";                         
                rhino_object.Attributes.SetUserString(defaultkey, defaultval);

                rhino_object.CommitChanges();

                var format = string.Format("F{0}", doc.DistanceDisplayPrecision);
                
                if (brep != null)
                    RhinoApp.WriteLine("Faces:{0},  Area:{1},  Volume:{2}",
                                            brep.Faces.Count,
                                            brep.GetArea().ToString(format),
                                            brep.GetVolume().ToString(format)
                                            );

                BrepFace topFace = brep.Faces.OrderByDescending(f => AreaMassProperties.Compute(f).Centroid.Z).ToList()[0];

                doc.Objects.AddSurface(topFace);

                Point3d center = AreaMassProperties.Compute(topFace).Centroid;
                               
                //HOW TO ASSIGN USER STRINGS TO BREPFACES?  ERROR SAYS THAT THERE IS NO ACCESSIBLE EXTENSION METHOD...             

                //topFace.Attributes.SetUserString(defaultkey, defaultval);

                //topFace.CommitChanges();


                //var str = string.Format("Face #", i.ToString());

                //TextDot dot = new TextDot(str, center[i]);

                //doc.Objects.AddTextDot(dot,attributes);

                TextDot textdot = new TextDot("Top Face", center);

                doc.Objects.AddTextDot(textdot, attributes);

            }

            return Result.Success;

I dont think you can label BrepFaces individually, since the UserText is part of the Rhino object while the BrepFace is part of geometry(Brep, Curve etc). There is a differentiation between geometry and the meta information saved in the object, which contains both information and geometry.

That said, you can add the face itself to the document as a single-faced brep, place a textdot on the centroid or maybe save the face id as a userstring to the brep object and get it from there.

Objects() returns an array. .Count is for lists, .Length is for arrays

Actually, you can attach user text or user data to pretty much anything that inherits from Rhino.Runtime.GeometryBase, and then some.

Here is an example of attaching user data to a Brep face.

SampleCsAddBrepFaceUserData.cs

Of course, you could add user text too.

– Dale

Dale,

Thanks for this, I’ll dig into this and try to implement into my command.

I see that you need this file for the sample above to work.

SampleCsUserDataObject.cs

Dan

Dale,

Here is a snip of my code:

BrepFace topFace = brep.Faces.OrderByDescending(f => AreaMassProperties.Compute(f).Centroid.Z).ToList()[0];

                topFace.SetUserString("testkey", "testval");

                //topFace.CommitChanges();

All is compiling and running, but the key/values are not be assigned to the BrepFace. Trying to add the .CommitChanges(); doesn’t apply here, correct?

Dan

Hi @daniel.depoe,

Looks like I may have mispoken. If you look at the example I referenced above, I’m attaching user data to the underlying surface of a BrepFace. See if that works any better.

– Dale