Getting ceilings and floors from Room

Getting ceilings and floors from Room

autodesk
Explorer Explorer
5,453 Views
7 Replies
Message 1 of 8

Getting ceilings and floors from Room

autodesk
Explorer
Explorer

Hi!

I have some room in selection and I need to get some informations about individual floors and ceilings (area and material) which are part of room.

I tried to use BoundarySegment and Faces of room, but i didn't get all elements wich part of the boundary, especially ceilings.

I don't won't to use a geometry, but i can't find any reliable solution.

 

Any suggestions?

 

Thanks!

0 Likes
5,454 Views
7 Replies
Replies (7)
Message 2 of 8

IbrahimNaeem
Advocate
Advocate

User Reference Intersector as per the following example taking from the RevitAPI.CHM which comes with the Revit API SDK Package:

public class RayProjection : IExternalCommand
{
    public Result Execute(ExternalCommandData revit, ref string message, ElementSet elements)
    {
        Document doc = revit.Application.ActiveUIDocument.Document;

        Selection selection = revit.Application.ActiveUIDocument.Selection;

        // If skylight is selected, process it.
        FamilyInstance skylight = null;
        if (selection.Elements.Size == 1)
        {
            foreach (Element e in selection.Elements)
            {
                if (e is FamilyInstance)
                {
                    FamilyInstance instance = e as FamilyInstance;
                    bool isWindow = (instance.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Windows);
                    bool isHostedByRoof = (instance.Host.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Roofs);

                    if (isWindow && isHostedByRoof)
                    {
                        skylight = instance;
                    }
                }
            }
        }

        if (skylight == null)
        {
            message = "Please select one skylight.";
            return Result.Cancelled;
        }

        // Calculate the height
        Line line = CalculateLineAboveFloor(doc, skylight);

        // Create a model curve to show the distance
        Plane plane = revit.Application.Application.Create.NewPlane(new XYZ(1, 0, 0), line.GetEndPoint(0));
        SketchPlane sketchPlane = SketchPlane.Create(doc, plane);

        ModelCurve curve = doc.Create.NewModelCurve(line, sketchPlane);

        // Show a message with the length value
        TaskDialog.Show("Distance", "Distance to floor: " + String.Format("{0:f2}", line.Length));

        return Result.Succeeded;
    }

    /// <summary>
    /// Determines the line segment that connects the skylight to the nearest floor.
    /// </summary>
    /// <returns>The line segment.</returns>
    private Line CalculateLineAboveFloor(Document doc, FamilyInstance skylight)
    {
        // Find a 3D view to use for the ReferenceIntersector constructor
        FilteredElementCollector collector = new FilteredElementCollector(doc);
        Func<View3D, bool> isNotTemplate = v3 => !(v3.IsTemplate);
        View3D view3D = collector.OfClass(typeof(View3D)).Cast<View3D>().First<View3D>(isNotTemplate);

        // Use the center of the skylight bounding box as the start point.
        BoundingBoxXYZ box = skylight.get_BoundingBox(view3D);
        XYZ center = box.Min.Add(box.Max).Multiply(0.5);

        // Project in the negative Z direction down to the floor.
        XYZ rayDirection = new XYZ(0, 0, -1);

        ElementClassFilter filter = new ElementClassFilter(typeof(Floor));

        ReferenceIntersector refIntersector = new ReferenceIntersector(filter, FindReferenceTarget.Face, view3D);
        ReferenceWithContext referenceWithContext = refIntersector.FindNearest(center, rayDirection);

        Reference reference = referenceWithContext.GetReference();
        XYZ intersection = reference.GlobalPoint;

        // Create line segment from the start point and intersection point.
        Line result = Line.CreateBound(center, intersection);
        return result;
    }
}

Hope this helps.

Thanks, Ibrahim Naeem 

0 Likes
Message 3 of 8

jeremytammik
Autodesk
Autodesk

Dear Petr,

 

Thank you for your query.

 

I like your handle 'Autodesk, CEO'  🙂

 

Please also look at the discussion on getting the ceiling material in a room:

 

http://forums.autodesk.com/t5/revit-api-forum/get-ceiling-material-in-room/m-p/6875603

 

I hope this helps.

 

Best regards,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 4 of 8

Anonymous
Not applicable

I realize this thread is old but I'm having an issue with something similar.

 

I'm having the user select a room, and I'm trying to get the ceiling of said room. I originally tried a filtered element collector grabbing ceilings, and then checking to see if the center point of the ceiling exists in my room's bounding box. I had an issue with the iterator saying it was out of elements, so i'm presuming none of the elements passed the comparison. Here's the snippet.

 

FilteredElementCollector ceilings = new FilteredElementCollector(doc).OfClass(typeof(Ceiling)).OfCategory(BuiltInCategory.OST_Ceilings);
			Element selectedCeiling = null;

			if (ceilings.Count() != 0)
			{
				foreach (Ceiling f in ceilings)
				{
					XYZ ceilingCenter = GetElementCenter(f);
					BoundingBoxXYZ roomBox = selectedRoom.get_BoundingBox(null);
					if (roomBox.Max.X >= ceilingCenter.X && roomBox.Min.X <= ceilingCenter.X && roomBox.Max.Y >= ceilingCenter.Y && roomBox.Min.Y <= ceilingCenter.Y && roomBox.Max.Z >= ceilingCenter.Z && roomBox.Min.Z <= ceilingCenter.Z)
					{
						selectedCeiling = f as Element;
					}
					selectedCeiling = f;
					XYZ ceilingCenter = GetElementCenter(f);
				}
			}

From there I searched the forums and saw this thread recommending using a reference intersector to ray cast into the ceiling. So, I tried this method as posted above, but I keep getting null references. I'm not sure if my type is wrong in the ElementClassFilter, or I thought maybe the co ordinate system in Revit might be y up instead of z up, but nothing seems to work. Here is the second snippet. 

 

XYZ roomCenter = GetElementCenter(selectedRoom);
			XYZ rayDirection = new XYZ(0, 0, 1);

			FilteredElementCollector collector = new FilteredElementCollector(doc);
			Func<View3D, bool> isNotTemplate = v3 => !(v3.IsTemplate);
			View3D View3D = collector.OfClass(typeof(View3D)).Cast<View3D>().First<View3D>(isNotTemplate);
			ElementClassFilter ceilingFilter = new ElementClassFilter(typeof(Ceiling));


			BoundingBoxXYZ roomBox = selectedRoom.get_BoundingBox(View3D);
			ReferenceIntersector ceilingIntersector = new ReferenceIntersector(ceilingFilter, FindReferenceTarget.Element, View3D);
			ReferenceWithContext referenceWithContext = ceilingIntersector.FindNearest(roomCenter, rayDirection);
			Reference ceilingRef = referenceWithContext.GetReference();
			Element selectedCeiling = doc.GetElement(ceilingRef) as Element;

Thanks for this,

0 Likes
Message 5 of 8

Anonymous
Not applicable

I solved the problem, but I just thought I would post the solution. The above ray casting works perfectly fine. The issue was one of the test rooms I was using had a courtyard in the center and the ray cast hit nothing and thus returned null.

 

I'm trying to think of a decent solution to this problem as there's no point within the BoundinBoxXYZ that would guarantee a hit. You could iterate moving further from the center until you hit something (the battleship method), but there's no guarantee that you'd be heading in a beneficial direction. I suppose you could pick random points within the room and probe them. Does anyone have any better ideas?

0 Likes
Message 6 of 8

jeremytammik
Autodesk
Autodesk

Thank you for explaining the problem and its cause.

 

I have no off-hand idea for a better solution.

 

One way to eliminate the problem without the need for probing would be:

 

  • Scan all the rooms and determine whether their profiles include any inner loops such as your courtyard.
  • For each floor with an inner loop, replace the room by a modified version with outer loops only.

 

Here is an example of reading the outline of a floor and creating a new floor using only the first, outer loop:

 

http://thebuildingcoder.typepad.com/blog/2008/11/editing-a-floor-profile.html

 

You would have to take more care to find all the outer profile loops, though... they may be more than one, and disjunct:

 

http://thebuildingcoder.typepad.com/blog/2017/10/disjunct-outer-loops-from-planar-face-with-separate...

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 7 of 8

Anonymous
Not applicable

Yeah that would definitely be a better solution. I've implemented the random probing (though you have to cap the count or you risk hanging the program indefinitely) but this is something I should implement down the road. The success of the probing method is linearly proportional to the area of the bounding box the room fills. large L hallways for example have a low success rate.

 

I suppose with any of these scripts there are assumptions on how the file was built and what geometry is accessible. Realistically they're not going to work in every fringe case.

 

Thanks for the insight.

0 Likes
Message 8 of 8

fazlul.db
Community Visitor
Community Visitor

Its strange. becouse when i  try get Celling like that, ReferanceInspector give me null.

but when i create floor upstears Celling, they give me floor, and igronre Celling

 

0 Likes