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 ![]()
exsurface_EW.gh (11.3 KB)
Maybe you can ask AI to transform this into code?
Regards, and have fun, Eef
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#!![]()
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.



