Create ViewSection from inclined surface – section always appears vertical even when transform basis is correct

Hi everyone,

I’m creating Revit Sections via Python (Revit 2024) using ViewSection_CreateSection, and I’m stuck at a behavior that seems inconsistent between mathematical transform correctness and Revit’s final section orientation.

I would really appreciate insight from anyone who has deep experience with section view transforms.

I want to:

  • Create a section view that looks straight at an inclined bottom surface

  • The section plane is defined by a surface normal

  • A curve lying on that surface should define the horizontal direction on screen

  • Result:

    • The surface should appear “flat”

    • The curve should be horizontal on screen

This works only when the surface is horizontal.
As soon as the surface is inclined in 3D, the resulting section view becomes vertical on screen, even though the transform basis seems correct.

  • curve : Dynamo curve lying on the surface

  • surfaceNormal : normal vector of the surface (correct direction)

  • midpoint : center of section

Everything is geometrically correct, but Revit seems to reinterpret or clamp the section orientation, especially when the surface is inclined.

I’m trying to understand whether:

  • This is a limitation

  • A missing constraint

  • Or a wrong assumption about how section transforms work

Any insight would be greatly appreciated :folded_hands:

# View normal (look direction)

viewZ = surfaceNormal.Normalize()

# Curve direction (should be horizontal on screen)

viewX = curveDirection.Normalize()

# Up direction

viewY = viewZ.CrossProduct(viewX).Normalize()

# Re-orthogonalize

viewX = viewY.CrossProduct(viewZ).Normalize()

# Build transform

t = Transform.Identity
t.Origin = midpoint
t.BasisX = viewX
t.BasisY = viewY
t.BasisZ = viewZ

Best to post the full Python to reproduce the issue - my gut tells me the method requires some conversion of parameters based on Revit’s expected sequencing, but as we don’t see where you’re calling the method and all the content leading to it we can only guess.

1 Like

Yes, Sir. This is code:

try:
	UIunit = Document.GetUnits(doc).GetFormatOptions(UnitType.UT_Length).DisplayUnits
except:
	UIunit = Document.GetUnits(doc).GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()


def tolist(input):
	if isinstance(input,list):
		return input
	else:
		return [input]

curves = tolist(IN[0])
viewFamilyType = IN[1]
height = UnitUtils.ConvertToInternalUnits(IN[2],UIunit)
offsetFromWall = UnitUtils.ConvertToInternalUnits(IN[3],UIunit)
depth_ = UnitUtils.ConvertToInternalUnits(IN[4],UIunit)
normalZ = IN[5]
if isinstance(normalZ, list):
    if len(normalZ) > 0:
        normalZ = normalZ[0]
watch = []

def DSCurveToViewSection(c, vft, offset, doc, height, depth):
    locC = c.ToRevitType()
    sp = locC.GetEndPoint(0)
    ep = locC.GetEndPoint(1)

    # Direction of curve
    v = ep.Subtract(sp)
    w = v.GetLength()
    lineDir = v.Normalize()
    startPoint = c.StartPoint
    endPoint = c.EndPoint

    lineDir = Vector.ByTwoPoints(endPoint, startPoint).ToXyz().Normalize()

    # Midpoint
    midpoint = sp.Add(v.Multiply(0.5))

    viewZ = normalZ.ToXyz().Normalize()
    # -----------------------------
    # Transform
    # -----------------------------
    viewX = lineDir
    viewY = viewZ.CrossProduct(viewX).Normalize()
    
    # Re-orthogonalize
    viewX = viewY.CrossProduct(viewZ).Normalize()

    t = Transform.Identity
    t.Origin = midpoint
    t.BasisX = viewX
    t.BasisY = viewY
    t.BasisZ = viewZ
    # -----------------------------
    # BoundingBox
    # -----------------------------
    vsBB = BoundingBoxXYZ()
    vsBB.Transform = t
    vsBB.Min = XYZ(-w / 2, 0, -offset)
    vsBB.Max = XYZ( w / 2, height, depth)
    # -----------------------------
    # Create section
    # -----------------------------
    section = ViewSection.CreateSection(doc, vft.Id, vsBB)
    return section, t

TransactionManager.Instance.EnsureInTransaction(doc)
for cvs in curves:
	watch.append(DSCurveToViewSection(cvs, viewFamilyType, offsetFromWall, doc,height,depth_))
TransactionManager.Instance.TransactionTaskDone

OUT = watch


This is dynamo picture !