Issues regarding the surface extension code in C#


I used AI to write a code for surface extension, which is currently functional. However, it doesn’t work correctly for non-quadrilateral surfaces. What I need is to extend all three edges of a triangular surface as shown in the image, rather than untrimming the triangle into a quadrilateral.
So, how should the code be modified to correctly extend a triangular surface?
exsurface.gh (6.2 KB)
Extend Surface.cs (6.7 KB)

I could not do it with AI or with coding, but happy to have found a way to do this with
Grasshopper :slight_smile:



exsurface_EW.gh (11.3 KB)

Maybe you can ask AI to transform this into code?

Regards, and have fun, Eef

I made it unnecessarily complex

exsurface_EW2.gh (7.0 KB)

Your Triangle is a trimmed Srf, a Brep so to say. When you rebuild your Srf with the 3 edges, it may behave as you expected. BTW you can paste C# code here, easier to have a look at.

You are using an untrimmed triangle. In this case you can just plug it directly into the Offset Component. I would try to avoid Region Union when possible, since it is quite expensive in computation time.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Rhino.Geometry;
using YatChun.Properties;

namespace Yatchun.Surface
{
    public class Yc_exsurface : GH_Component
    {
        public Yc_exsurface()
          : base("Extend Surface", "ExtendSrf",
                "Extend surface from edge (preserves trim state)",
                "Yatchun", "Surface")
        {
        }

        public override Guid ComponentGuid => new Guid("A3B4C5D6-E7F8-495A-B2C3-D4E5F6A7B8C9");
        protected override Bitmap Icon => Resources.Yc_Extend_Srf;

        protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddSurfaceParameter("Surface", "S", "Surface to extend", GH_ParamAccess.item);
            pManager.AddNumberParameter("West Edge", "W", "Length to extend west edge (0 for no extension)", GH_ParamAccess.item, 0);
            pManager.AddNumberParameter("South Edge", "S", "Length to extend south edge (0 for no extension)", GH_ParamAccess.item, 0);
            pManager.AddNumberParameter("East Edge", "E", "Length to extend east edge (0 for no extension)", GH_ParamAccess.item, 0);
            pManager.AddNumberParameter("North Edge", "N", "Length to extend north edge (0 for no extension)", GH_ParamAccess.item, 0);
            pManager.AddBooleanParameter("Extension Type", "T", "Set True for smooth extension, False for ruled", GH_ParamAccess.item, true);
        }

        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
            pManager.AddSurfaceParameter("Surface", "S", "Extended surface", GH_ParamAccess.item);
        }

        protected override void SolveInstance(IGH_DataAccess DA)
        {
            // 1. Get input surface
            GH_Surface ghSrf = null;
            if (!DA.GetData(0, ref ghSrf) || ghSrf == null)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Invalid surface input");
                return;
            }

            // 2. Get underlying Brep
            var brep = ghSrf.Value as Brep;
            if (brep == null)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input is not a valid Brep");
                return;
            }

            // 3. Check if trimmed
            bool isTrimmed = !brep.IsSurface;
            if (isTrimmed)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning,
                    "Note: Extended surface will be untrimmed. Original trim boundaries will be lost.");
            }

            var firstSurface = brep.Surfaces.First();
            Rhino.Geometry.Surface srf = firstSurface as Rhino.Geometry.Surface;
            if (srf == null)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error,
                    "Input surface is not a valid NURBS surface");
                return;
            }

            // 4. Check if triangle surface
            bool isTriangle = false;
            if (srf is NurbsSurface nurbsSrf)
            {
                isTriangle = (nurbsSrf.Points.CountU == 3 || nurbsSrf.Points.CountV == 3);
                if (isTriangle)
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Remark,
                        "Input is a triangle surface - only south edge can be extended");
                }
            }

            // 5. Duplicate surface (safe way)
            var duplicated = srf.Duplicate() as Rhino.Geometry.Surface;
            if (duplicated == null)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error,
                    "Surface duplication failed");
                return;
            }
            srf = duplicated;

            // 6. Get extension parameters
            double wEdge = 0, sEdge = 0, eEdge = 0, nEdge = 0;
            bool smooth = true;

            if (!DA.GetData(1, ref wEdge)) wEdge = 0;
            if (!DA.GetData(2, ref sEdge)) sEdge = 0;
            if (!DA.GetData(3, ref eEdge)) eEdge = 0;
            if (!DA.GetData(4, ref nEdge)) nEdge = 0;
            if (!DA.GetData(5, ref smooth)) smooth = true;

            // 7. For triangle surfaces, only allow south edge extension
            if (isTriangle)
            {
                if (wEdge > 0 || eEdge > 0 || nEdge > 0)
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Warning,
                        "Triangle surfaces can only be extended from the south edge. Other edges ignored.");
                    wEdge = eEdge = nEdge = 0;
                }
            }

            // 8. Perform extension
            try
            {
                if (wEdge > 0) srf = srf.Extend(IsoStatus.West, wEdge, smooth);
                if (sEdge > 0) srf = srf.Extend(IsoStatus.South, sEdge, smooth);
                if (eEdge > 0) srf = srf.Extend(IsoStatus.East, eEdge, smooth);
                if (nEdge > 0) srf = srf.Extend(IsoStatus.North, nEdge, smooth);

                // 9. Output extended surface
                DA.SetData(0, new GH_Surface(srf));
            }
            catch (Exception ex)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error,
                    $"Extension failed: {ex.Message}");
            }
        }

        // Helper method to determine edge type (from ext.txt)
        private IsoStatus DetermineEdgeType(Rhino.Geometry.Surface surface, Rhino.Geometry.Curve curve)
        {
            // Set surface domains to 0-1 for consistent evaluation
            surface.SetDomain(0, new Interval(0, 1));
            surface.SetDomain(1, new Interval(0, 1));

            // Get start and end points of the curve
            Point3d startPt = curve.PointAtStart;
            Point3d endPt = curve.PointAtEnd;

            // Find closest points on surface
            double u1, v1, u2, v2;
            surface.ClosestPoint(startPt, out u1, out v1);
            surface.ClosestPoint(endPt, out u2, out v2);

            // Determine if edge is along U or V direction
            const double tolerance = 0.0001;
            bool isUEdge = Math.Abs(v1 - v2) < tolerance;
            bool isVEdge = Math.Abs(u1 - u2) < tolerance;

            if (isUEdge)
            {
                if (v1 < tolerance) return IsoStatus.South;
                if (v1 > 1.0 - tolerance) return IsoStatus.North;
            }
            else if (isVEdge)
            {
                if (u1 < tolerance) return IsoStatus.West;
                if (u1 > 1.0 - tolerance) return IsoStatus.East;
            }

            return IsoStatus.None;
        }
    }
}

Thank you, but I’m currently researching how to write .gha components using C#!:grinning_face:

If you really want to learn to write code, I would recommend writing the code and not just copy+pasting from AI. I think you can use the generated code as a good basis, but you should try to understand exactly what is happening. In turn, it helps if you write a lot of code yourself.