Access and Align Top & Bottom Ceiling Grid Lines

Hi everyone,

I’ve been searching through the forum for a way to extract ceiling grid lines, but I haven’t found a clear solution yet.

I’m currently working in Revit 2023. My ceiling has two grid patterns: one above the structure and one below it. What I need is to retrieve both sets of grid lines and then align the top grid lines to match the bottom ones.

I tried the Sparrow package, but it only returns the bottom grid lines. From what I understand, starting in Revit 2025 it’s possible to access ceiling grid lines through the Revit API (and via the Rhythm package), but these also seem to return only the bottom grid lines.

So my questions are:

  • Is it possible in Revit 2023 to access and align the top ceiling grid lines to the bottom ones using Dynamo or any workaround?

  • If not, does the Revit 2025 API actually allow access to both top and bottom ceiling grid lines, and has anyone successfully aligned them?

Any insight or example would be greatly appreciated. I attached my compund ceiling.

Test-Ceiling.rvt (4.9 MB)

Thanks in advance!

Unfortunately there is currently no easy API access to the ceiling grid - The geometry has only been accessible since 2025.3

It may be possible, because if you can dimension a pattern, you can also create an alignment using Document.Create.NewAlignment() method

See discussion here (archive.org) → The Building Coder: Link Filter, Ceiling and Link Hatch Dim Voodoo

2 Likes

yes genius loci have some nodes for get reference from grid lines and for dimension, it was that method i use before it comes with api for split ceiling in patterns…know it was dirty but works :wink: :wink:

PS sparrow have some nodes as well for pattern lines…but it isnt so stable…IMO and doesnt work if perimeter curves is curved

In your image, what do you mean by ‘top and bottom’ grid lines?

1 Like

i can only get bottom grids, probably just me :wink: but you could project them to top surface maybe :wink:

Hi Jacob. In the image im showing the top face of the ceiling and the bottom face. Both of them have the same finish pattern but I move the grids on the top one. I want to know a way to align the top grid lines to the bottom, to make them equal.

I understand that I have to get the grid references to make an alignment :thinking: but how I get those refereces?

Hi Sovitek. Yes thats the problem. But projecting them I only get the lines, not the references to align, maybe there’s something i dont see. I think I have to try Genius Loci nodes. I will try later, thanks.

So… Is one of those view a plan view looking down at the ceiling and the other a ceiling plan view looking up at the ceiling? They’re both named “level 1” so I don’t know which view you’re in.

yeah give it try, and you could try just created projected curves as model/detail curves and dimension/reference them and delete in same run…hahahh…not at dyn in the moment, so just a guess :wink: :wink: you know all that xmas run :wink: :wink:

Yes, the left one is the ceiling plan and the right is the floor plan

1 Like

Well I could get the bottom ceiling grid references using Compound Pattern Reference, but not the top grid references.

As a workaround, I manually create a dimensions between top grid lines and extract their references. Then, using Dimension ByReferences, I create a dimension between the top grid reference and the bottom grid reference.

Even with both references and the resulting dimension, I don’t know how to align or move the top grid to match the bottom grid. I also tried setting the generated dimension value to zero, but couldn’t make it work.

I’ve attached the Dynamo file, Revit file, and images. Any ideas would be appreciated.

Top face:

Bottom face:

Script result (the projected curves are the green ones)

Test_Ceiling.rvt (3.4 MB)

CeilingTiles_1.2.dyn (90.6 KB)

Thanks!

1 Like

Yeah as mention, last time i play around with it i could only get ref from bottom, not at dynamo today and probably first next week, and i can take a look if you not have find a solution, but no guarantee i can do it :wink: :wink:

Just a wild guess,think genius loci have some nodes for fillpattern elements as well…maybe it could help…not sure have never tried

I’m off for the holidays, but will drop this here

Grabs the top and bottom faces and then pulls the hatch line references

Based on the Genius Loci nodes with some clean ups and the Building Coder link I posted above

:dove:

import clr

clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument


def get_grid_count(item, face_ref):
    face_geom = item.GetGeometryObjectFromReference(face_ref)
    material = doc.GetElement(face_geom.MaterialElementId)
    pattern_type = doc.GetElement(material.SurfaceForegroundPatternId)
    pattern = pattern_type.GetFillPattern()
    return pattern.GridCount


def get_stable_ref(face_ref, grid_count):
    stable_ref = face_ref.ConvertToStableRepresentation(doc)
    ref_strings = [
        f"{stable_ref}/{a + 2 * b * grid_count}" for a in range(1, 3) for b in range(2)
    ]
    return [
        Reference.ParseFromStableRepresentation(doc, ref_string)
        for ref_string in ref_strings
    ]


elements = UnwrapElement(IN[0]) if isinstance(IN[0], list) else [UnwrapElement(IN[0])]

align_to_bottom = IN[1]

if not align_to_bottom:
    align_to_bottom = True  # For later use

for item in elements:
    top_face = next(iter(HostObjectUtils.GetTopFaces(item)))

    bottom_face = next(iter(HostObjectUtils.GetBottomFaces(item)))

    top_grid_count = get_grid_count(item, top_face)
    bottom_grid_count = get_grid_count(item, bottom_face)

    top_refs = get_stable_ref(top_face, top_grid_count)
    bottom_refs = get_stable_ref(bottom_face, bottom_grid_count)

    TransactionManager.Instance.EnsureInTransaction(doc)

    # Do alignment in here

    TransactionManager.Instance.TransactionTaskDone()

OUT = top_refs, bottom_refs
3 Likes

Nice one :wink: and enjoy your holidays…merry x mas :wink:

1 Like

Thanks a lot.

I’m able to get both references. In the script I create two ref planes in X and Y direction to align their references (rpRef) to the top grid references (top_refs). Then align the rpRef to the bottom_refs.

This works but it deppends of how the ceiling is drawn I think. I have to get the direction of the top_refs and bottom_refs to classify them and align the correct rpRef.

I attached what Im trying (it didn’t worked). How I can separate the top_refs and bottom_refs in X and Y to have correct alignment?

import clr

# ----------------------------
# Revit API
# ----------------------------
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *

# ----------------------------
# Revit Services
# ----------------------------
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
view = doc.ActiveView


# ------------------------------------------------
# Get grid count from surface pattern
# ------------------------------------------------
def get_grid_count(item, face_ref):
    face_geom = item.GetGeometryObjectFromReference(face_ref)
    material = doc.GetElement(face_geom.MaterialElementId)
    pattern_type = doc.GetElement(material.SurfaceForegroundPatternId)
    pattern = pattern_type.GetFillPattern()
    return pattern.GridCount


# ------------------------------------------------
# Hatch references (stable references)
# ------------------------------------------------
def get_pattern_refs(face_ref, grid_count):
    stable = face_ref.ConvertToStableRepresentation(doc)
    refs = []

    for b in range(grid_count):
        for a in range(1, 3):
            idx = a + 2 * b * grid_count
            refs.append(
                Reference.ParseFromStableRepresentation(
                    doc, stable + "/" + str(idx)
                )
            )
    return refs

# ------------------------------------------------
# Create a valid vertical ReferencePlane
# ------------------------------------------------
def create_vertical_refplane(origin, direction, view):
    dir_xy = XYZ(direction.X, direction.Y, 0).Normalize()

    # Horizontal base line
    p0 = origin
    p1 = origin.Add(dir_xy.Multiply(10.0))

    # Vertical cut vector
    cutVec = XYZ.BasisZ

    return doc.Create.NewReferencePlane(p0, p1, cutVec, view)


# ------------------------------------------------
# INPUT
# ------------------------------------------------
elements = UnwrapElement(IN[0]) if isinstance(IN[0], list) else [UnwrapElement(IN[0])]


# ------------------------------------------------
# MAIN
# ------------------------------------------------
TransactionManager.Instance.EnsureInTransaction(doc)

created_planes = []

for item in elements:

    # Faces
    top_face_ref = next(iter(HostObjectUtils.GetTopFaces(item)))
    bottom_face_ref = next(iter(HostObjectUtils.GetBottomFaces(item)))

    top_face = item.GetGeometryObjectFromReference(top_face_ref)

    # Pattern
    grid_count = get_grid_count(item, top_face_ref)

    top_refs = get_pattern_refs(top_face_ref, grid_count)
    bottom_refs = get_pattern_refs(bottom_face_ref, grid_count)

    origin = top_face.Origin

    # =================================================
    # X DIRECTION
    # =================================================
    refs_top_X = top_refs[0:2]
    refs_bot_X = bottom_refs[0:2]

    rp_X = create_vertical_refplane(origin, XYZ.BasisX, view)
    rpRef_X = rp_X.GetReference()

    for t_ref, b_ref in zip(refs_top_X, refs_bot_X):
        doc.Create.NewAlignment(view, t_ref, rpRef_X)
        doc.Create.NewAlignment(view, b_ref, rpRef_X)

    created_planes.append(rp_X)

    # =================================================
    # Y DIRECTION (cross pattern)
    # =================================================
    if grid_count > 1:
        refs_top_Y = top_refs[2:4]
        refs_bot_Y = bottom_refs[2:4]

        rp_Y = create_vertical_refplane(origin, XYZ.BasisY, view)
        rpRef_Y = rp_Y.GetReference()

        for t_ref, b_ref in zip(refs_top_Y, refs_bot_Y):
            doc.Create.NewAlignment(view, t_ref, rpRef_Y)
            doc.Create.NewAlignment(view, b_ref, rpRef_Y)

        created_planes.append(rp_Y)


TransactionManager.Instance.TransactionTaskDone()


# ------------------------------------------------
# OUTPUT
# ------------------------------------------------
OUT = created_planes

Thanks a lot!