Hi,
The Revit API has methods for retrieving points from a point cloud instance (filtered around a plane list).
A simple solution (though not necessarily very accurate) :
- Obtain the points on the face with
PointCloudInstance.GetPoints(). - Then, if there are any, compare the UV coordinates of the center of a face with the average UV coordinates from the point clouds.

Python code (works only with PythonNet3 or IronPython3, due to the use of Linq extensions)
Code
import clr
import sys
import System
from System.Collections.Generic import List
clr.AddReference('ProtoGeometry')
import Autodesk.DesignScript.Geometry as DS
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
# Revit API
clr.AddReference('RevitAPI')
import Autodesk
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Analysis import *
from Autodesk.Revit.DB.PointClouds import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
# Current Revit document
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
def view_prepare(view):
"""
Prepare the given 3D view for analysis visualization.
Parameters
----------
view : Autodesk.Revit.DB.View
The Revit view where analysis results will be displayed.
"""
# Try to find existing analysis display style by name using a collector and LINQ predicate
analysisDisplayStyle = FilteredElementCollector(doc).OfClass(AnalysisDisplayStyle)\
.FirstOrDefault(System.Func[DB.Element, System.Boolean](lambda x : x.Name == "Paint_Preview"))
#
if view.AnalysisDisplayStyleId == ElementId.InvalidElementId or analysisDisplayStyle is None :
TransactionManager.Instance.EnsureInTransaction(doc)
coloredSurfaceSettings = AnalysisDisplayColoredSurfaceSettings()
coloredSurfaceSettings.ShowGridLines = False
#
colorSettings = AnalysisDisplayColorSettings()
legendSettings = AnalysisDisplayLegendSettings()
legendSettings.ShowLegend = False
#
analysisDisplayStyle = AnalysisDisplayStyle.CreateAnalysisDisplayStyle( doc, "Paint_Preview", coloredSurfaceSettings, colorSettings, legendSettings )
view.AnalysisDisplayStyleId = analysisDisplayStyle.Id
# Commit transaction
TransactionManager.Instance.TransactionTaskDone()
def color_face_analysis(view, lst_face_analysis):
"""
Color faces in a view using Spatial Field Manager analysis results.
Parameters
----------
view : Autodesk.Revit.DB.View
Target view for spatial field analysis coloring.
lst_face_analysis : list[tuple]
Iterable of (face, face_ref, is_align_to_pts_cloud) where:
- face : Autodesk.Revit.DB.Face
- face_ref : Autodesk.Revit.DB.Reference
- is_align_to_pts_cloud : bool, True if points are aligned to the face.
"""
sfm = SpatialFieldManager.GetSpatialFieldManager(view)
if sfm is None:
sfm = SpatialFieldManager.CreateSpatialFieldManager(view, 1)
# Clear previous primitives
sfm.Clear()
# Register a result schema once
resultSchema = AnalysisResultSchema("Schema Name" , "Preview_Selection")
schemaIndex = sfm.RegisterResult(resultSchema)
# Iterate faces to create/update spatial field primitives
for face, face_ref, is_align_to_pts_cloud in lst_face_analysis:
idx = sfm.AddSpatialFieldPrimitive(face_ref)
uvPts = List[UV]()
bbuv = face.GetBoundingBox()
uvPts.Add(bbuv.Min)
uvPts.Add(bbuv.Max)
pnts = FieldDomainPointsByUV(uvPts)
if is_align_to_pts_cloud:
doubleListA = List[System.Double]([0.0])
else:
doubleListA = List[System.Double]([10.0])
vals = FieldValues(List[ValueAtPoint]([ValueAtPoint(doubleListA), ValueAtPoint(doubleListA)]))
sfm.UpdateSpatialFieldPrimitive(idx, pnts, vals, schemaIndex)
toList = lambda x : x if hasattr(x, "__iter__") and not isinstance(x, (str, System.String)) else [x]
# Inputs from Dynamo
point_cloud = UnwrapElement(IN[0])
tf = point_cloud.GetTransform()
lst_walls = toList(UnwrapElement(IN[1]))
threeDview = UnwrapElement(IN[2])
# Parameters controlling point sampling and tolerance
margin_factor_box = 0.05
averageDistance = 0.1
numberOfPoints = 2000
outPoints = []
debug = []
analysis_faces = []
view_prepare(threeDview)
for wall in lst_walls:
bbx_wall = wall.get_BoundingBox(None)
line_wall = wall.Location.Curve
for layerType in [ShellLayerType.Interior, ShellLayerType.Exterior]:
# create planes on each faces (6 faces)"
Iplanes=List[DB.Plane]()
#
ref_face = DB.HostObjectUtils.GetSideFaces(wall, layerType).First()
face = wall.GetGeometryObjectFromReference(ref_face)
#
bbox_uv = face.GetBoundingBox()
center_uv = (bbox_uv.Min + bbox_uv.Max) / 2.0
origin = face.Evaluate(center_uv)
faceNormal = face.FaceNormal
#
planeA = DB.Plane.CreateByNormalAndOrigin(faceNormal.Negate(), origin + faceNormal * margin_factor_box)
planeB = DB.Plane.CreateByNormalAndOrigin(faceNormal, origin - faceNormal * margin_factor_box)
Iplanes.Add(planeA)
Iplanes.Add(planeB)
#
planeC = DB.Plane.CreateByNormalAndOrigin(XYZ(0,0,1), bbx_wall.Min)
planeD = DB.Plane.CreateByNormalAndOrigin(XYZ(0,0,-1), bbx_wall.Max)
Iplanes.Add(planeC)
Iplanes.Add(planeD)
#
planeF = DB.Plane.CreateByNormalAndOrigin(line_wall.GetEndPoint(0), line_wall.Direction)
planeG = DB.Plane.CreateByNormalAndOrigin(line_wall.GetEndPoint(1), line_wall.Direction.Negate())
Iplanes.Add(planeF)
Iplanes.Add(planeG)
#
pcf = PointCloudFilterFactory.CreateMultiPlaneFilter(Iplanes)
#
points = point_cloud.GetPoints(pcf, averageDistance, numberOfPoints)
xyz_pts = List[XYZ]([tf.OfPoint(XYZ(p.X, p.Y, p.Z)) for p in points])
# Linq conversion
# XYZ -> IntersectionResult -> filter by distance -> DB.UV
uvPoints = xyz_pts.Where(System.Func[DB.XYZ, System.Boolean](lambda pt : abs((pt - origin).DotProduct(faceNormal)) < averageDistance))\
.Select[DB.XYZ, DB.IntersectionResult](System.Func[DB.XYZ, DB.IntersectionResult](lambda pt : face.Project(pt) ))\
.Where(System.Func[DB.IntersectionResult, System.Boolean](lambda interR : interR is not None))\
.Where(System.Func[DB.IntersectionResult, System.Boolean](lambda interR : interR.Distance < averageDistance))\
.Select[DB.IntersectionResult, DB.UV](System.Func[DB.IntersectionResult, DB.UV](lambda interR : interR.UVPoint ))\
.ToList()
#
# there are not enough points
if len(uvPoints) < 100:
analysis_faces.append([face, ref_face, False])
else:
# Linq aggregate function to compute sum and average UV
sum_uv = uvPoints.Aggregate[DB.UV, DB.UV](
UV.Zero, # accumulate init UV
System.Func[DB.UV, DB.UV, DB.UV](
lambda acc_uv, next_uv: acc_uv + next_uv
)
)
average_uv = sum_uv / len(uvPoints)
analysis_faces.append([face, ref_face, center_uv.DistanceTo(average_uv) < 2 ])
color_face_analysis(threeDview, analysis_faces)