Hello Rhino developers,
I have encountered a limitation in the RhinoCommon API that prevents automation of certain modeling workflows. I would like to request an enhancement to the API.
Problem description:
In the Rhino UI, the _SplitEdge
command allows users to split a Brep edge at specified points, resulting in new separate edges within the Brep topology.
However, in the public API (RhinoCommon, both C# and Python), there is currently no way to programmatically split a Brep edge so that new edges appear in the Brep.
The method BrepEdge.Split(t)
only returns a curve, but does not alter the underlying Brep topology or create new edges on the Brep.
Why this matters:
- It is not possible to create an automated plugin that splits Brep edges at intersection points with curves or surfaces in one step.
- For complex models with many edges, manual splitting is extremely time-consuming and error-prone.
- Duplicating edges as curves and splitting those curves does not solve the problem, since the result is just a set of auxiliary curves, not new Brep edges.
Here is a sample C# code illustrating an attempt to programmatically split Brep edges using RhinoCommon:
using System.Collections.Generic;
using Rhino;
using Rhino.Commands;
using Rhino.DocObjects;
using Rhino.Geometry;
using Rhino.Input.Custom;
using Rhino.Geometry.Intersect;
public class SplitMultiEdgesCommand : Command
{
public SplitMultiEdgesCommand()
{
Instance = this;
}
public static SplitMultiEdgesCommand Instance { get; private set; }
public override string EnglishName => "SplitMultiEdges";
protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
// 1. Select edges (SubObject)
var go = new GetObject();
go.SetCommandPrompt("Select edges (sub-objects)");
go.GeometryFilter = ObjectType.EdgeFilter;
go.SubObjectSelect = true;
go.EnablePreSelect(false, true);
go.GetMultiple(1, 0);
if (go.CommandResult() != Result.Success)
return go.CommandResult();
List<ObjRef> edge_refs = new List<ObjRef>();
for (int i = 0; i < go.ObjectCount; i++)
edge_refs.Add(go.Object(i));
if (edge_refs.Count == 0)
return Result.Cancel;
// 2. Select splitting curves
var go2 = new GetObject();
go2.SetCommandPrompt("Select curves to split edges");
go2.GeometryFilter = ObjectType.Curve;
go2.DeselectAllBeforePostSelect = false;
go2.GetMultiple(1, 0);
if (go2.CommandResult() != Result.Success)
return go2.CommandResult();
List<Curve> knives = new List<Curve>();
for (int i = 0; i < go2.ObjectCount; i++)
{
var crv = go2.Object(i).Curve();
if (crv != null)
knives.Add(crv.DuplicateCurve());
}
if (knives.Count == 0)
return Result.Cancel;
// 3. Map: for each brep -> list of edges for split and objectId
var brepEdgeMap = new Dictionary<Brep, List<int>>();
var brepGuidMap = new Dictionary<Brep, System.Guid>();
foreach (var edgeRef in edge_refs)
{
var edge = edgeRef.Edge();
var brep = edge.Brep;
if (!brepEdgeMap.ContainsKey(brep))
{
brepEdgeMap[brep] = new List<int>();
var brepObjId = edgeRef.ObjectId; // <--- IMPORTANT: take ObjectId from ObjRef!
brepGuidMap[brep] = brepObjId;
}
brepEdgeMap[brep].Add(edge.EdgeIndex);
}
// 4. For each Brep work with the required edges
foreach (var kv in brepEdgeMap)
{
var brep = kv.Key;
var edgeIndices = kv.Value;
var brepGuid = brepGuidMap[brep];
// For each edge — find intersection points with splitting curves
foreach (int ei in edgeIndices)
{
var edge = brep.Edges[ei];
var edgeCurve = edge.DuplicateCurve();
List<double> splitParams = new List<double>();
foreach (var knife in knives)
{
var x = Intersection.CurveCurve(edgeCurve, knife, doc.ModelAbsoluteTolerance, doc.ModelAbsoluteTolerance);
if (x != null)
foreach (var it in x)
{
double t;
if (edgeCurve.ClosestPoint(it.PointA, out t))
splitParams.Add(t);
}
}
if (splitParams.Count > 0)
{
// Remove duplicates and sort
splitParams.Sort();
for (int i = splitParams.Count - 1; i > 0; i--)
if (System.Math.Abs(splitParams[i] - splitParams[i - 1]) < doc.ModelAbsoluteTolerance)
splitParams.RemoveAt(i);
// Split the edge
foreach (var t in splitParams)
{
edge.Split(t);
}
}
}
// Update the object in the document
doc.Objects.Replace(brepGuid, brep);
}
doc.Views.Redraw();
RhinoApp.WriteLine("Done!");
return Result.Success;
}
}
Requested functionality:
- Addition of a public method in the RhinoCommon API to programmatically split Brep edges, analogous to the
_SplitEdge
command in the Rhino UI. - The ability to specify an edge (or a set of edges) and a list of parameters/points at which to split, resulting in new edges in the Brep topology.
- Support for automatic updating of document objects.
Example use case:
- The user selects multiple Brep edges and splitting curves/knives.
- A plugin finds intersection points and calls a SplitEdge function at those points.
- The document is updated with a new Brep containing the newly created edges.
Thank you for your attention to this issue!
Any comments or plans regarding extending the API in this direction would be greatly appreciated.
Best regards,
leex