HiddenLineDrawing: Clipping Planes are ignored in Make2D calculation (C#)

I am developing a custom Make2D command using Rhino.Geometry.HiddenLineDrawing in C#. I have successfully implemented the standard hidden line drawing (Visible/Hidden curves), but I am stuck on getting Clipping Planes to work.

The Goal: I want to generate a 2D drawing of selected objects that respects the active Clipping Planes in the view, exactly like the native Rhino Make2D command does with “Clipping Plane Intersections” enabled.

The Issue: Even though I am adding the active clipping planes to the HiddenLineDrawingParameters and enabling OccludingSectionOption, the Compute method returns the full, uncut geometry. It acts as if the clipping planes do not exist.

What I have tried:

  1. Checking that the Clipping Plane is active in the specific Viewport ID.

  2. Setting hldParams.OccludingSectionOption = true.

  3. Adding the plane using hldParams.AddClippingPlane(plane).

  4. I have tried Global Clipping (implicit) and Selective Clipping using hldParams.AddGeometryAndPlanes(...).

In all cases, the output is the complete object, ignoring the cut.

Code Snippet: Here is the core logic I am using to set up the parameters. Am I missing a step to “link” the planes to the view or the geometry?

public void CalculateMake2D(Rhino.Display.RhinoViewport viewport, List objects)
{
var hldParams = new HiddenLineDrawingParameters
{
AbsoluteTolerance = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance,
IncludeTangentEdges = false,
IncludeHiddenCurves = true,
Flatten = true, // We want the 2D projection
OccludingSectionOption = true // Enabled for solid caps
};

hldParams.SetViewport(new ViewportInfo(viewport));

// 1. Find and Add Active Clipping Planes
List<Plane> activePlanes = new List<Plane>();
foreach (var obj in RhinoDoc.ActiveDoc.Objects.FindByObjectType(ObjectType.ClipPlane))
{
    if (obj is ClippingPlaneObject cp && cp.ClippingPlaneGeometry.ViewportIds().Contains(viewport.Id))
    {
        // The plane is active in this view
        var plane = cp.ClippingPlaneGeometry.Plane;
        activePlanes.Add(plane);
        
        // Register the plane
        hldParams.AddClippingPlane(plane);
    }
}

// 2. Add Geometry
foreach (var objRef in objects)
{
    var geo = objRef.Geometry();
    if (geo is Brep || geo is Mesh)
    {
        // Attempt 1: Selective Clipping (Explicitly passing the planes)
        if (activePlanes.Count > 0)
        {
             // Using overload: (geometry, xform, id, occluding_sections, clips)
             hldParams.AddGeometryAndPlanes(geo, Transform.Identity, objRef.ObjectId, true, activePlanes);
        }
        // Attempt 2: Standard Add (hoping for implicit global clipping)
        else 
        {
             hldParams.AddGeometry(geo, Transform.Identity, objRef.ObjectId, true);
        }
    }
}

// 3. Compute
// The result here contains all curves (Visible/Hidden) but NO intersection cuts 
// and the geometry is fully drawn as if uncut.
var hld = HiddenLineDrawing.Compute(hldParams, true);

// ... Processing hld.Segments ...

}

@rajaa - can you help with this?

1 Like

I think the only missing part is that the direction of the clipping-plane need to be flipped before registering. Try the following:

List<Plane> activePlanes = new List<Plane>();
foreach (var obj in RhinoDoc.ActiveDoc.Objects.FindByObjectType(ObjectType.ClipPlane))
{
    if (obj is ClippingPlaneObject cp && cp.ClippingPlaneGeometry.ViewportIds().Contains(viewport.Id))
    {
        // The plane is active in this view
        var plane = cp.ClippingPlaneGeometry.Plane;
        plane.Flip();
        activePlanes.Add(plane);

        // Register the plane
        hld_params.AddClippingPlane(plane);

        // Check the depth
        if (cp.ClippingPlaneGeometry.PlaneDepthEnabled)
        {
              Plane p1 = new Plane(plane);
              p1.Translate(-cp.ClippingPlaneGeometry.PlaneDepth * plane.Normal);
              p1.Flip();
              activePlanes.Add(p1);

              // Register the depth plane
              hld_params.AddClippingPlane(p1);
        }
    }
}

Hi @rajaa,

Thank you so much for your complete answer regarding the Clipping Planes! Your solution worked perfectly, and I was able to get the section cuts generating exactly as needed. I really appreciate the help.

Since getting that working, I have run into a new discrepancy between my custom HiddenLineDrawing engine and the native Make2D command, and I was hoping you might have some insight into this as well.

Depending on the view (Plan, Side, etc.), my custom engine is dropping certain segments (missing curves) and occasionally generating extra curves that the native Make2D command cleanly resolves.

Here are the visual comparisons between my engine’s output and Rhino’s native Make2D: *
*

My Current Setup: I am setting up a virtual RhinoViewport, pointing it at the bounding box of my geometry, and feeding it into HiddenLineDrawingParameters. I am using the following key parameters:

  • Flatten = true

  • OccludingSectionOption = true

  • AbsoluteTolerance = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance

  • Recursively adding geometry to handle nested InstanceReferenceGeometry (Blocks).

  • Calling hld.RejoinCompatibleVisible() after computing.

Below is the core of my engine class :

Make2DEngine.cs (7.1 KB)

What does the native Make2D command do under the hood to resolve these missing/extra segments that HiddenLineDrawing.Compute misses with my current parameters? Is there a specific tolerance multiplier, a viewport configuration trick, or a pre/post-processing step on the geometry (like intersecting coplanar faces) that I need to implement to achieve exact parity with the native command?

Thanks again for your time!

From a quick review, I do not see that Make2D does anything different than your steps. It uses model tolerance as you do with no pre or post processing that I can see.

Check if there is some pattern to these missing pieces (for example, it happens at tight tolerances, or with clipping-planes, or when objects are part of a block, etc.)