Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 99 additions & 22 deletions Revit_Core_Engine/Query/Space.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical;
using BH.oM.Base.Attributes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
Expand All @@ -35,12 +36,14 @@ public static partial class Query
/**** Public methods ****/
/***************************************************/

[PreviousVersion("9.1", "BH.Revit.Engine.Core.Query.Space(Autodesk.Revit.DB.Element, System.Collections.Generic.IEnumerable<Autodesk.Revit.DB.Mechanical.Space>, System.Boolean)")]
[Description("Returns the Revit Space that contains the given element.")]
[Input("element", "The Revit element for which to find the containing Space.")]
[Input("spaces", "An optional collection of Revit Spaces to search. If not provided, all Spaces in the element's document will be used.")]
[Input("useRoomCalculationPoint", "If true and the element is a FamilyInstance with a spatial element calculation point, that point will be used for containment checks.")]
[Input("findClosestIfNotContained", "If true, if no containing Space is found, the method will attempt to find the closest Space in the direction of the element's connectors or below it.")]
[Output("space", "The Revit Space containing the element, or the element itself if it is a Space. Returns null if no containing Space is found.")]
public static Space Space(this Element element, IEnumerable<Space> spaces, bool useRoomCalculationPoint = false)
public static Space Space(this Element element, IEnumerable<Space> spaces, bool useRoomCalculationPoint = false, bool findClosestIfNotContained = false)
{
if (element == null)
return null;
Expand All @@ -49,41 +52,83 @@ public static Space Space(this Element element, IEnumerable<Space> spaces, bool
if (element is Space space)
return space;

// 2. If the element is a FamilyInstance, try the .Space property
if (element is FamilyInstance fi && fi.Space != null)
// 2a. Check physical location - space property of family without calculation point
FamilyInstance fi = element as FamilyInstance;
if (fi != null && !fi.HasSpatialElementCalculationPoint && fi.Space != null)
return fi.Space;

// 3. Use location point and check which space contains it
if (spaces == null)
{
Document doc = element.Document;
spaces = new FilteredElementCollector(doc)
.OfClass(typeof(SpatialElement))
.OfType<Space>()
.ToList();
}
else
{
m_LinkTransforms = spaces.GroupBy(s => s.Document)
.Where(g => g.Key.IsLinked)
.ToDictionary(g => g.Key, g => g.Key.LinkInstance().GetTotalTransform());
}

XYZ locationPoint = element.LocationPoint(useRoomCalculationPoint);
if (locationPoint == null)
// 2b. Check physical location - location point of the element
XYZ locationPoint = element.LocationPoint(false);
if (locationPoint == null)
return null;

// Transform location point if element is from linked document
Transform elementTransform = element.Document.IsLinked ? element.Document.LinkInstance().GetTotalTransform() : Transform.Identity;
if (!elementTransform.IsIdentity)
locationPoint = elementTransform.OfPoint(locationPoint);

// Collect spaces and their transforms if from linked documents
if (spaces == null)
spaces = new FilteredElementCollector(element.Document).OfClass(typeof(SpatialElement)).OfType<Space>().ToList();
else
m_LinkTransforms = spaces.GroupBy(s => s.Document).Where(g => g.Key.IsLinked).ToDictionary(g => g.Key, g => g.Key.LinkInstance().GetTotalTransform());

foreach (var sp in spaces)
{
if (locationPoint.IsInSpace(sp))
return sp;
}

// 4. Not found
// 3. Use room calculation point and check which space contains it
if (fi != null && fi.HasSpatialElementCalculationPoint && useRoomCalculationPoint)
{
if (fi.Space != null)
return fi.Space;

XYZ roomCalcPoint = element.LocationPoint(useRoomCalculationPoint);
if (roomCalcPoint == null)
return null;

if (!elementTransform.IsIdentity)
roomCalcPoint = elementTransform.OfPoint(roomCalcPoint);

foreach (var sp in spaces)
{
if (roomCalcPoint.IsInSpace(sp))
return sp;
}
}

if (!findClosestIfNotContained)
return null;

// 4. If not found, try find closest space in connector directions (for MEP elements)
var connectors = element.Connectors()?.OrderByDescending(x => x.GetMEPConnectorInfo().IsPrimary).ToList();
if (connectors != null && connectors.Any())
{
foreach (var conn in connectors)
{
XYZ connPoint = conn.Origin;
XYZ connDirection = conn.CoordinateSystem.BasisZ;

if (!elementTransform.IsIdentity)
{
connPoint = elementTransform.OfPoint(connPoint);
connDirection = elementTransform.OfVector(connDirection);
}

Space foundClosest = connPoint.FindClosestSpaceInDirection(connDirection, spaces, maxDistance: 3); // 3 feet max distance
if (foundClosest != null)
return foundClosest;
}
}

// 5. If still not found, try find closest below (negative Z direction)
Space foundClosestBelow = locationPoint.FindClosestSpaceInDirection(-XYZ.BasisZ, spaces, maxDistance: 10); // 10 feet max distance
if (foundClosestBelow != null)
return foundClosestBelow;

// Not found
return null;
}

Expand All @@ -101,6 +146,38 @@ private static bool IsInSpace(this XYZ locationPoint, Space space)
return space.IsPointInSpace(locationPoint);
}

/***************************************************/

private static Space FindClosestSpaceInDirection(this XYZ startPoint, XYZ direction, IEnumerable<Space> spaces, double maxDistance)
{
if (startPoint == null || direction == null || spaces == null || maxDistance <= 0)
return null;

// Ensure direction is normalized
XYZ normalizedDirection = direction.Normalize();

// Calculate step size and number of steps based on maxDistance
double stepSize = 1.0; // 1 feet step size
int maxSteps = (int)Math.Ceiling(maxDistance / stepSize);

for (int step = 1; step <= maxSteps; step++)
{
double distance = step * stepSize;
if (distance > maxDistance) break;

XYZ testPoint = startPoint.Add(normalizedDirection.Multiply(distance));

// Check if this point along the ray is in any space
foreach (Space space in spaces)
{
if (testPoint.IsInSpace(space))
return space;
}
}

return null; // No space found within max distance
}

/***************************************************/
/**** Private field ****/
/***************************************************/
Expand Down