Built some Python to find a “Logical Center Point” of a non-standard planar surface, based on tessellating the geometry. It’s not perfect by any means, but it may be useful for finding the center of rooms which have non-standard geometry (i.e. U shaped, L shaped, O shaped, etc.).
The two outputs are the center of the longest non-boundary tessellated edge, and the center of the largest triangle after tessellation. Your results may vary, but it might be a bit easier than what PointAtParameter gives for stuff like centering a room.
The Python script
########################################
############## Properties ##############
########################################
__author__ = 'Jacob Small'
__version__ = '0.1.0'
__description__ = "Attempts to find the logical center of a surface via tessellation"
__DynamoBuilds__ = "3.1"
__ReleaseNotes__ = """Not tested completely. Test thuroughly before using in production."""
__Dependancies__ = "none"
__Copyright__ = "2023, Autodesk Inc."
__license__ = "Apache 3.0"
########################################
### Configure the Python environment ###
########################################
### standard imports ###
import sys #add the sys class to the Python environment so we can work with the sys objects
import clr #add the CLR (common language runtime) class to the Python environment so we can work with .net libraries
import math #add the Math class to the Python environment so we can do math stuff
import random #add the Random module to the Python environment so we can impersonate a squirrel
### basic Dynamo imports ###
clr.AddReference('ProtoGeometry') #add the Dynamo geometry library to the CLR
from Autodesk.DesignScript import Geometry as DG #add the Dynamo geometry class using the alias DG, to ensure no overlap between class calls in Revit or Dynamo
clr.AddReference('DSCoreNodes') #add the standard Dynamo library to the CLR
import DSCore # import the standard Dynamo classes to the Python environment
clr.AddReference('GeometryColor') #add the Geometry color library to the CLR
from Modifiers import GeometryColor #import the GeometryColor class to the Python environment
import Dynamo.Visualization as DV #import the Dynamo visualization class so we can use it to tessellate
#########################################
###### Global variables and inputs ######
#########################################
srfs = IN[0] #data from the IN[0] port of the Dynamo environment
if srfs.__class__ != list: srfs = [srfs] #ensure the surfaces are a list
results = [] #an empty list to hold the results
#########################################
############## Code begins ##############
#########################################
for srf in srfs: #for each surface
### extract basic properties of the surface ###
nrml = srf.NormalAtParameter(0.5,0.5) #the normal of the surface
perim = DG.PolySurface.ByJoinedSurfaces([i.Extrude(nrml) for i in srf.PerimeterCurves()]) #the perimeter curves of the surface as a polysurface
### build the render package to tessellate the Dynamo geometry ###
renderPackageFactory = DV.DefaultRenderPackageFactory() #buld a render package factor
renderPackage = renderPackageFactory.CreateRenderPackage() #create the render package
tessellationParams = renderPackageFactory.TessellationParameters #get the tessellation parameters
tessellate = srf.Tessellate(renderPackage, tessellationParams) #tessellate the surface
### get the tessellation results ###
verts = renderPackage.MeshVertices #get the verticies from the tessellation in the render package
verts = [verts[i:i+3] for i in range(0, len(verts), 3)] #slice the tessellation into tripples
verts = [DG.Point.ByCoordinates(i[0],i[1],i[2]) for i in verts] #generate points from the tripples
tris = [verts[i:i+3] for i in range(0, len(verts), 3)] #slice the verts into tripples
tris = [DG.Polygon.ByPoints(i) for i in tris] #generate polygons from the tripples
tris = [i.Patch() for i in tris] #generate surfaces from the polygons
### get the center of the longest non-bounding edge ###
polySurf = DG.PolySurface.ByJoinedSurfaces(tris) #convert the surfaces into a polysurface
edges = [i.CurveGeometry for i in polySurf.Edges] #get the edges as curves
edges = [i for i in edges if not i.PointAtParameter(0.5).DoesIntersect(perim)] #filter out edges which have a midpoint that intersects the perimeter of the surface
lengths = [i.Length for i in edges] #get the lengths
srt = sorted([i for i in lengths]) #get the lengths as a sorted list
srt = [lengths.index(i) for i in srt] #get the index of each of the sorted lines
edge = edges[srt[-1]] #get the edge at the last index from the sorted list of indexes - the longest edge
edgePnt = edge.PointAtParameter(0.5) #get the midpoint of the edge
### get the center of the largest triangle ###
areas = [i.Area for i in tris] #get the areas of the triangles
srt = sorted([i for i in areas]) #get the areas as a new sorted list
srt = [areas.index(i) for i in srt] #get the index ofeach of the areas
tri = tris[srt[-1]] #Get the triangle at the last index - the largest area
triPnt = tri.PointAtParameter(0.5,0.5) #get the midpoint of the triangle
### append the points to the results ###
results.append([edgePnt,triPnt])#add the edge point and tripoint to the results
#########################################
##### Return the results to Dynamo ######
#########################################
OUT = results #return the results to the Dynamo environment