Is there a convenient way with RhinoCommon to convert a Brep that is known to consist of a single, simple loop into a Curve? I see that I can access the Brep’s trims as Curves, but I can’t find a convenient way to stitch them together. The best I can come up with is creating a new PolyCurve and calling Append for each trim, but that seems clunky.

Are you looking for a RhinoCommon way to do what the DupBorder command does?

I believe so. I’m still not totally familiar with the Rhino geometry model, but that looks right.

```
Brep b; // defined elsewhere
RhinoDoc doc; // the current document
double tolerance = doc.ModelAbsoluteTolerance;
Curve[] nakedEdges = b.DuplicateEdgeCurves(true);
Curve[] joined = Curve.JoinCurves(nakedEdges, 2.1*tolerance);
if (joined.Length < 1 || joined.Length > 1)
{
RhinoApp.WriteLine("Joining of curves failed....");
}
else
{
Curve loop = joined[0];
}
```

Thank you! For curiosity’s sake, what’s the basis of the 2.1 factor?

Each curve was created with a certain tolerance. When joining you have to bridge that value twice in the worst case, so a factor of 2.1 seems safe. It is also used often in examples by McNeel.

To be more certain, I typically use this method with a much larger maximumTolerance:

```
public static bool TryJoinCurves(IEnumerable<Curve> curves, out Curve result, double maximumTolerance = 1e-2)
{
if (null == curves || !curves.Any())
{
result = null;
return false;
}
int n = -8;
// attempt to join with increasing tolerances
Curve[] joined = curves.ToArray();
double tolerance = Math.Pow(10, n);
while (tolerance <= maximumTolerance && null != joined && joined.Length > 1)
{
// Ignore curves that are shorter than the tolerance,
// if this is not done, it may lead to incorrect joins
double tol = tolerance;
IEnumerable<Curve> noShortCurves = curves.Where(c => c.GetLength(1e-8) > tol);
joined = Curve.JoinCurves(noShortCurves, tol, false);
tolerance = Math.Pow(10, ++n);
}
// if at some point joined has become null return false
if (null == joined)
{
result = curves.First();
return false;
}
// if the length of joined allows it, set the first curve as the result
result = joined.Length > 0 ? joined[0].ToNurbsCurve() : curves.First();
return joined.Length == 1;
}
```

Here is a quick and dirty “DupBorder” equivalent:

```
Rhino.DocObjects.ObjectType filter = Rhino.DocObjects.ObjectType.Surface | Rhino.DocObjects.ObjectType.PolysrfFilter;
Rhino.DocObjects.ObjRef objref = null;
Rhino.Commands.Result rc = Rhino.Input.RhinoGet.GetOneObject("Select surface or polysurface", false, filter, out objref);
if (rc != Rhino.Commands.Result.Success || objref == null)
return rc;
Rhino.DocObjects.RhinoObject rhobj = objref.Object();
Rhino.Geometry.Brep brep = objref.Brep();
if (rhobj == null || brep == null)
return Rhino.Commands.Result.Failure;
rhobj.Select(false);
System.Collections.Generic.List<Rhino.Geometry.Curve> curves = new System.Collections.Generic.List<Rhino.Geometry.Curve>();
foreach (Rhino.Geometry.BrepEdge edge in brep.Edges)
{
// Find only the naked edges
if (edge.Valence == Rhino.Geometry.EdgeAdjacency.Naked)
{
Rhino.Geometry.Curve crv = edge.DuplicateCurve();
if (null != crv)
curves.Add(crv);
}
}
double tol = 2.1 * doc.ModelAbsoluteTolerance;
Rhino.Geometry.Curve[] output = Rhino.Geometry.Curve.JoinCurves(curves, tol);
for (int i = 0; i < output.Length; i++)
{
Guid id = doc.Objects.AddCurve(output[i]);
doc.Objects.Select(id);
}
doc.Views.Redraw();
```