Profile Orientation

Hi All,
I wondered if I could get some help with the attached file…

basically i want both the curves’ normals be aligned to WCS Z-Axis, outer profile be oriented clockwise and inner to be oriented counter clockwise.

thanks in advance.

orientation-woe.3dm (34.5 KB)

Hi @reuben.alfred,

Technically, curves do not have normals. However, if a curve is planar and we can determine the plane a curve lies on, then the curve’s “normal” is really just the curve plane’s normal direction or z-axis.

As I have mentioned before, curves have direction. And, the plane of a closed planar curve is calculated from it’s tangent direction. Thus, if you have two co-planar curves, one oriented clockwise and one counter-clockwise, then it is not possible for them to both have planes with an normal in the world z-axis direction.

Let me know if this helps.

– Dale

Thanks for that Dale,

OK, the reason I asked for opposite orientation is that when I used EyeShot it required the inner and outer orientation to be in opposite senses.

Please take a look at the attached files, the closed curve orientations are same and they are codirectional to point3d.ZAxis yet it wont extrude properly.

The code is here too.

           private Result test5(RhinoDoc doc)
	BuilderUtil.getOuterAndInnerProfiles(doc, out var outer, out var inners);

	var extrusion =FenemateExtrusion.create(outer, inners.ToArray(), 200);

	if (extrusion != null) doc.Objects.Add(extrusion);
	return Result.Success;

    public static bool getOuterAndInnerProfiles(RhinoDoc doc, out Curve outer, out List<Curve> inners)
	var curves = new List<Curve>();
	outer  = null;
	inners = null;
	foreach (var docob in doc.Objects)
		if (!(docob.Geometry is Curve curve)) continue;
		if (!curve.IsClosed) continue;

	if (curves.Count == 0) return false;
	Curve outerCurve = curves[0];;
	for (var i = 0; i < curves.Count -1; )
		var doIncrease = true;
		outerCurve = curves[i];

		if (!outerCurve.TryGetPlane(out var plane)) continue;
		for (var j = i +1; j < curves.Count; j++)
			var nextCurve = curves[j];
			var rc        = Curve.PlanarClosedCurveRelationship(outerCurve, nextCurve, plane, RhinoMath.ZeroTolerance);

			switch (rc) {
				case RegionContainment.Disjoint:
				case RegionContainment.MutualIntersection:
				case RegionContainment.BInsideA: continue;

			if (rc != RegionContainment.AInsideB) continue;

			i          = j;
			doIncrease = false;

		if (doIncrease) i++;

	if (curves.Count >1)
		var index = curves.IndexOf(outerCurve);

		if (index < 0) return false;


	outer  = outerCurve;
	inners = curves;

	return true;

    public static Extrusion create(Curve outerProfile, Curve[] innerProfiles, double height)
	return extrude(outerProfile, innerProfiles, height);

   private static Extrusion extrude(Curve outerProfile, Curve[] innerProfiles, double height)
	if (null == outerProfile || height <= RhinoMath.ZeroTolerance) return null;
	if (!outerProfile.TryGetPlane(out var plane)) return null;

	//height*= (plane.Normal.IsParallelTo(Vector3d.ZAxis)!= BuilderUtil.Constants.IsCoDirectional)? 1: -1;
	var path = new Line {From = plane.PointAt(0.0, 0.0, 0.0), To = plane.PointAt(0.0, 0.0, height)};

	if (!path.IsValid || !(path.Length > RhinoMath.ZeroTolerance)) return null;

	var up      = plane.YAxis;
	var tangent = path.To - path.From;

	if (!up.IsValid || !up.IsUnitVector || Math.Abs(up * tangent) > RhinoMath.SqrtEpsilon) return null;

	var transform = Transform.PlaneToPlane(plane, Plane.WorldXY);
	var curve     = outerProfile.DuplicateCurve();
	if (curve.ClosedCurveOrientation(plane.ZAxis) == CurveOrientation.Clockwise) curve.Reverse();

	foreach (var profile in innerProfiles)
		if (profile.ClosedCurveOrientation(plane.ZAxis) == CurveOrientation.CounterClockwise) profile.Reverse();

	var extrusion = new Extrusion();
	extrusion.SetOuterProfile(curve, true);

	foreach (var profile in innerProfiles)
		if (!profile.TryGetPlane(out var curvePlane)) continue;
		if (!plane.isCoplanar(curvePlane, RhinoMath.ZeroTolerance)) continue;

		curve = profile.DuplicateCurve();

	extrusion.SetPathAndUp(path.From, path.To, up);
	var translate = (plane.Normal.IsParallelTo(Vector3d.ZAxis) != BuilderUtil.Constants.IsCoDirectional) ? new Vector3d(0, 0, height) : Vector3d.Zero;

	return extrusion.IsValid ? extrusion : null;

Hi @reuben.alfred,

After you’ve validated your input curves as being planar, closed, etc., if you find one of the curves oriented clockwise (or if it’s plane’s normal is pointing opposite of the world z-axis, then reverse the direction of the curve using Curve.Reverse, before extruding.

– Dale

Hi Date,

Thanks and tried everything I could and the result is that some get extruded correctly and some dont.

I cannot find anything obvious to my understanding.

please find the screenshot and the code thats handling extrusion:


extrude.cs (2.2 KB)

Hi Dale,

The strategy for extruding is as follows, AFAIU, please correct me if i am wrong:

  1. find the outer curve, ensure it is counter-clockwise wrt Vector3d.ZAxis?
    2 find all inner curves: for each inner curve ensure the curve is counter-clockwise wrt Vector3d.ZAxis
  2. create Extrusion object
  3. set outer profile
  4. set inner profiles
  5. set path and up vector

this is what my code expresses, I think?