Create TopoSolid from a list of point with concave hull

Create TopoSolid from a list of point with concave hull

YorickSOHO
Contributor Contributor
547 Views
4 Replies
Message 1 of 5

Create TopoSolid from a list of point with concave hull

YorickSOHO
Contributor
Contributor

Hello everyone,

 

My goal is to achive through the API, the same worflow my users do through the UI :

1 - Create a Toposolid from a list of points (CSV)

2 - Move it 

3 - Rotate it

 

I am able to do all that but the Toposild.Create(document, List(XYZ), ElementId, ElementId) returns the convex hull of my list of points, which doesn't resemble the one I get through the UI.

I suppose the UI uses the Toposolid.Create(document, List(CurveLoop), List(XYZ), ElementId, ElementId) and finds somehow a concave hull for the points which is passed in as a CurveLoop but I couldn't find the Method or a way to get the same concave hull.

 

Any help would be greatly appreciated,

Regards

0 Likes
548 Views
4 Replies
Replies (4)
Message 2 of 5

jeremy_tammik
Alumni
Alumni

Have you looked at the new Revit SDK sample Toposolid that was introduced to demonstrate use of the new functionality? It may have been implemented especially for you and your use case. Unfortunately, taking a look at it right now, I discover that its readme file and comments all require editing. Anyway, reading the information on What's New in the Revit 2024 API and searching for Toposolid, I can see that all the functionality you can possibly desire is available:

  

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 3 of 5

YorickSOHO
Contributor
Contributor

Hi, 
Sorry for the delay, couldn't find the time to work on this topic again.

 

Thank you for your reply Jeremy but I couldn't find a solution to my issue in the SDK.
I would like to have access to the algorithm wich converts my list of points (from CSV) to a Curveloop when using Revit user interface but through the API.

 

To further expand my post, here is what I get using Revit API :
RevitAPI.jpg

 

Here is what I get doing the same process by hand and what I want to get through the API :

UserInterface.jpg

Attached is the Dynamo graph I use to read the CSV and getting my Zmin and list of (x,y,z)
And below the commented python code :

import clr
import sys
import System
import math
from System.Collections.Generic import List

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

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

doc = DocumentManager.Instance.CurrentDBDocument
TransactionManager.Instance.ForceCloseTransaction()

#Get units to make script unit agnostic
unitLen = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()

#Getting Project Base point and its components
PtBase = BasePoint.GetProjectBasePoint(doc)
PtBaseAngle = PtBase.get_Parameter(BuiltInParameter.BASEPOINT_ANGLETON_PARAM).AsDouble()
PtBaseX = UnitUtils.ConvertFromInternalUnits(PtBase.get_Parameter(BuiltInParameter.BASEPOINT_EASTWEST_PARAM).AsDouble(), unitLen)
PtBaseY = UnitUtils.ConvertFromInternalUnits(PtBase.get_Parameter(BuiltInParameter.BASEPOINT_NORTHSOUTH_PARAM).AsDouble(), unitLen)

#Retrieving baseLevel
lvls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).WhereElementIsNotElementType().ToElements()
for l in lvls :
    if l.Elevation == 0 :
        Level0 = l
        break

#Entries : List of XYZ points and Zmin
Pts = IN[0]
zMin = IN[1]

#List to log user outputs. Should be able to use the graph through DynamoPlayer
output = []

#Initiating .Net list
PointsTopo = List[XYZ]()

#Converting csv XYZ to .net list of points
for i in range(len(Pts)) :
    pt = Pts[i]
    #checks if x y z components exist
    if len(pt) != 3:
        output.append("Line "+str(i+2)+" ignored, empty value in csv")

    else :
        #Modify components to take into account sharedCoordinates, internal origin and basePoint are the same
        try :
            xPt = float(pt[0])
            yPt = float(pt[1]) 
            zPt = float(pt[2])-zMin
            ptXYZ = XYZ(
            UnitUtils.ConvertToInternalUnits(xPt-PtBaseX,unitLen),
            UnitUtils.ConvertToInternalUnits(yPt-PtBaseY,unitLen),
            UnitUtils.ConvertToInternalUnits(zPt,unitLen)
            )
            PointsTopo.Add(ptXYZ)
        #In case one component can't be converted to float
        except :
            output.append("Line "+str(i+2)+" ignored, value is not a number")

#Starting transaction
TransactionManager.Instance.EnsureInTransaction(doc)

#If at least one API point has been created
if ptXYZ :
    #Retrieving default Toposolid type, waiting for ElementTypeGroup implementation for toposild
    typeTopoId = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Toposolid).WhereElementIsElementType().ToElements()[0].Id

    #Creating the toposolid
    newTopo = Toposolid.Create(doc ,PointsTopo, typeTopoId, Level0.Id)
    
    #zMin is toposolid elevation, subtracted when creating points
    elevTopo = newTopo.GetParameter(ForgeTypeId("autodesk.revit.parameter:toposolidHeightabovelevelParam-1.0.0"))
    elevTopo.Set(UnitUtils.ConvertToInternalUnits(zMin,unitLen))
    
    #Rotation so it matches trueNorth 
    locationTopo = newTopo.Location
    rotAxis = Line.CreateUnbound(XYZ(0,0,0),XYZ(0,0,1))
    locationTopo.Rotate(rotAxis,PtBaseAngle)
else :
    output = "Unable to create Toposolid"

#Ending transaction
TransactionManager.Instance.TransactionTaskDone()
TransactionManager.Instance.ForceCloseTransaction()

#Give user feedback
if output :
    OUT = output
else :
    OUT = "Toposolid created without error"

 

Is there a method I can use to get the exact same concave Curveloop that I get through Revit buttons from my points ?

 

Thank you to anyone who can take the time to help me out,

0 Likes
Message 4 of 5

jeremy_tammik
Alumni
Alumni

Look at the list of three overloaded Toposolid constructors:

  

  

You seem to be using the one that just takes a list of points and has no further information about them that would be required in order to create a concave curve loop. You ned to use a different constructor and provide the full concave curve loop information in order to achieve your goal.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 5 of 5

YorickSOHO
Contributor
Contributor

That's what I meant in my first post. I want to use this one : 

Create Method (Document, IList(CurveLoop), IList(XYZ), ElementId, ElementId)


I can compute a concave hull for my list of points but it's an implementation "from scratch" and I have little chance to get the same result as what I get manually.

Because concave hull computation can give many many different results (from convex to each point on edge)


Is there a way to retrieve the same "points to concave hull" function that Revit uses ?


So I can feed the my Toposolid.Create method a CurveLoop and the list of points.

0 Likes