Creating a mass from Toposurface geometry

Following up this suggestion from Julien, here’s a look at how to create a massing family with geometry built from the mesh of a toposurface.

public void topoToMass()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;
    Application app = this.Application;

    TopographySurface topo = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element)) as TopographySurface;

    Mesh mesh = topo.get_Geometry(new Options()).First(q => q is Mesh) as Mesh;

    Document famDoc = app.NewFamilyDocument(@"C:\ProgramData\Autodesk\RAC 2014\Family Templates\English_I\Conceptual Mass\Mass.rft");

    using (Transaction t = new Transaction(famDoc,"Create massing surfaces"))
    {
        t.Start();

        for (int i = 0; i < mesh.NumTriangles; i++)
        {
            MeshTriangle mt = mesh.get_Triangle(i);
            makeForm(famDoc, mt.get_Vertex(0), mt.get_Vertex(1), mt.get_Vertex(2));

            if (i > 0 && i % 100 == 0)
            {
                TaskDialog td = new TaskDialog("Form Counter");
                td.CommonButtons = TaskDialogCommonButtons.Yes|TaskDialogCommonButtons.No;
                td.MainInstruction = i + " out of " + mesh.NumTriangles + " triangles processed. Do you want to continue?";
                if (td.Show() == TaskDialogResult.No)
                    break;
            }
        }
        t.Commit();
    }
    famDoc.LoadFamily(doc);
}

private Form makeForm(Document doc, XYZ pt1, XYZ pt2, XYZ pt3)
{
    Form form = null;

    XYZ u = pt2.Subtract(pt1);
    XYZ v = pt3.Subtract(pt1);
    double area = u.CrossProduct(v).GetLength()/2;
    if (area < 10)
        return null;

    ReferenceArray ra = new ReferenceArray();
    ra.Append(MakeCuveByPoints(doc, pt1, pt2).GeometryCurve.Reference);
    ra.Append(MakeCuveByPoints(doc, pt2, pt3).GeometryCurve.Reference);
    ra.Append(MakeCuveByPoints(doc, pt3, pt1).GeometryCurve.Reference);

    form = doc.FamilyCreate.NewFormByCap(true, ra);

    return form;
}

private CurveByPoints MakeCuveByPoints(Document doc, XYZ ptA, XYZ ptB)
{
    ReferencePointArray rpa = new ReferencePointArray();
    rpa.Append(doc.FamilyCreate.NewReferencePoint(ptA));
    rpa.Append(doc.FamilyCreate.NewReferencePoint(ptB));
    return doc.FamilyCreate.NewCurveByPoints(rpa);
}

31 thoughts on “Creating a mass from Toposurface geometry

  1. Hi, this looks extremely useful thanks for sharing the code. I am unable to run the code though, it always throws an exception when I go to load the family ‘famDoc.LoadFamily(doc);’? Do you know whats going wrong and how I can fix it? The exception is ‘Revit encountered a System.Runtime.InteropServices.SEHException: External component has thrown an exception.’

    The code is exactly like your sample except that the path to ‘Mass.rft’ is different on my computer (using Revit 2014 in Australia); theres no ‘C:\ProgramData\Autodesk\RAC 2014\Family Templates\English_I\Conceptual Mass\Mass.rft’ but I do have ‘C:\ProgramData\Autodesk\RVT 2014\Family Templates\English\Conceptual Mass\Metric Mass.rft’ which is what I am using. Maybe this is the cause of the error?

          • If I do that and save the family to a .rfa. Then open the .rfa it looks correct, just like your example. See the image (the top image is the TS I selected and the bottom is the mass created): http://i58.tinypic.com/5yaweu.jpg

            I guess my options are either to:
            – Save the family to a file, read that family in using LoadFamily() or LoadFamilySymbol(), then delete the .rfa
            – Regenerate the family document before calling load family. That might work, I’ll give it a try.

      • Also I am running the addin in a ‘Construction Revit Project’ not a ‘Family Project’ or MEP or etc. Maybe that affects it?

  2. Harry,

    I was running in to the same issue as above. It turns out the problem was the LoadFamily was returning this error if you didn’t have SP2 installed. Installed that and the program runs without a hitch.

  3. Hi Harry,

    I have a question. Is there any way where we can code the api so that it is placed exactly on the topo-surface. I might need to create the mass and discard the topo-surface. Please advise.

    Thanks

    Ani

    • The easiest way would be to use the Element.Location property, but that property has no data for a toposurface. Perhaps it would work to match the BoundingBox.Min or Max points for the topo and family instance.

  4. Hello,

    I have been trying to get this macro to work in Revit 2014 (service pack 3) and I keep getting the error “Revit failed to execute topoToMass”
    Here is the full code I am compiling: http://pastebin.com/cPHpmiHk

    Your help is very much appreciated to help me figure what I am missing or have left out.

    I am working on a project with a very large site and a lot of topography that I need to convert to roads (slabs). I want to convert the Revit Toposurface to a mass and then apply a slab to the surface of the mass. The roads have parts of the project under them so I need to make the roads with slabs in Revit.

    Thank you!

    Ry

      • Hello,

        Thanks for the quick reply. The error message displays:
        —–
        Revit failed to execute topoToMass.

        A problem has been detected.

        Autodesk.Revit.Exceptions.ArgumentException: Input
        templateFileName doesn’t exist.
        at MacroModule.executeMacro_(MacroModule* , AString*
        macroName)
        at MacroModule.executeMacro(MacroModule* , AString* )
        at
        UIMacroGeneralManager.runMacro(UIMacroGeneralManager* ,
        MacroModule* pModule, AString* macroName)
        —–

        I did try to go through the code to figure it out but keep getting this same error message each time. The only line of code I changed from your code above was the location of the conceptual mass folder on my system. This leads me to believe that I must have something wrong in the format or possibly the other code above the macro script.

        Your help is very much appreciated. Thank you!

  5. Hello Harry,

    That was it actually! There was a tiny bit of the code that was off. On my system the template file is named: Metric Mass.rft

    In your example code it was just Mass.rft

    Note to others who may try to use the sample code. Be sure to check the whole template file path as well as the template file name. On my system the full path with template file location is: C:\ProgramData\Autodesk\RVT 2014\Family Templates\English\Conceptual Mass\Metric Mass.rft

    I fixed the template path to make sure it was correct and forgot to check the actually template file name.

    Thanks again for your help to get it working.

    Now I need to deal with being able to create a slab from the mass that I created from the toposurface. Harry, do you know of a better way to do that? I am now finding that the toposurface may be too complex to make a slab from the mass. Might work if I take the topography into Rhino and tweak it in there and then bring it back into Revit.

  6. Thanks alot,
    this is my trial and came up with this error please help
    Mass contains only mesh geometry, which can’t be used to compute Mass Floors, volume, or surface area.

  7. ey thanks so much for posting! Looks great, I just cant seem to get it working in Rev2018…

    I get errors on these three lines:
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;
    Application app = this.Application;

    ‘Topo.ThisDocument’ does not contain a definition for ‘ActiveUIDocument’ and no extension method ‘ActiveUIDocument’ accepting a first argument of type ‘Topo.ThisDocument’ could be found (are you missing a using directive or an assembly reference?) (CS1061) – C:\Users\maxxb\AppData\Local\Temp\{AED54BB2-C242-4070-8F4F-8EC5A41BECEA}\Revit\DocHookups9928\1389522539520\Topo\Source\Topo\ThisDocument.cs:49,25

    ‘Topo.ThisDocument’ does not contain a definition for ‘ActiveUIDocument’ and no extension method ‘ActiveUIDocument’ accepting a first argument of type ‘Topo.ThisDocument’ could be found (are you missing a using directive or an assembly reference?) (CS1061) – C:\Users\maxxb\AppData\Local\Temp\{AED54BB2-C242-4070-8F4F-8EC5A41BECEA}\Revit\DocHookups9928\1389522539520\Topo\Source\Topo\ThisDocument.cs:50,29

    Cannot implicitly convert type ‘Autodesk.Revit.UI.UIApplication’ to ‘Autodesk.Revit.ApplicationServices.Applction’ (CS009)

    Any ideas?

    Apologies in advance if these seem daft – I am an absolute beginner when it comes to Macros.

    Many thanks.

    Max

    • Hi Max,

      Did you create a document macro. If so, try an Application Macro. Also, if you would like to learn more about the API, my video course is a great way to quickly learn a whole lot about it. bitly.com/revitapi

      Regards
      Harry

      • No sorry – I set up an application macro :/

        Should I try a document macro?

        I’ve started to watch your videos – very useful!

        Still need to get this one working at a pinch though – do you have any idea what the errors are referring to?

        I should explain, my last post is possibly not clear – the three lines are listed first, and their corresponding errors are listed in order, below.

        Many thanks.

        M

  8. Hi,

    Very detailed and clear sample. I would like to go a step further, generating a a solid object limited by a base horizontal plane and the topo. I would also like to be able to perform boolean operations in Revit between two solids generated using this method. Is the Revit mass adequate for achieving what I need? Can I produce a 3D solid defining all the edges of the solid under a triangle (as in the sample) and using ” form = doc.FamilyCreate.NewFormByCap(true, ra)”?

    Thanks!!

    • great questions – neither the mass nor the toposurface have the solid geometry that you need to do that, but there may be some other solution. Is your toposurface created by XYZ points or contour lines in an import?

      • Hi Harry,

        I usually deal with imported landxml files, using the Site designer to import them. However, I can export them from Civil 3D as autocad contour lines if needed (at the cost of loosing some precision)
        I have found what seems to be a solution in Dynamo, but it is not clear how to transform the dynamo solid into Revit solid https://forum.dynamobim.com/t/create-a-solid-from-a-topography/6001/17
        Appartently, other way to deal with it may be to create conceptual masses and perform the boolean operations through FormIt.

        I can also try to generate Autodesk 3D solids, from surfaces but I still need to perform the boolean operations between them.

        By the way, have you got any Revit API courses/trining, apart from the two at Udemy? I plan to get trained on the topic, starting at the end of September (still getting familiarized with some base Revit topics)

        Regards,

        Juan

        • I fogot to explain that, before exporting the landxml file from Civil, I use to select the 3D triangle style. The triangle vertexes are available once transformed onto a Revit toposurface.

  9. Hi, thanks for sharing your code, great work! Is there a way to extrude all the triangles of the created mass along the z-axis.

  10. Afternoon Harry, first of all, thanks for this code, is been really helpful. I’ve been doing a couple of dense vegetation, but a weird thing happened. I’m working with the 2020 but in the afternoon I had to download the 2018 and from that didn’t worked anymore. I’m still working with the 2020 but now I’ve got the same error that Ry Bruscoe had. ;( I’ve checked the folder location many times now. Do you know what can be.
    Thank you so much

    Rita

    • Hi Rita,

      If you are getting a “file does not exist” error, please check that this path used for NewFamilyDocument is correct. You could add some logic to check if (File.Exists(filename).. if that might help.

      Harry

      Document famDoc = app.NewFamilyDocument(@”C:\ProgramData\Autodesk\RAC 2014\Family Templates\English_I\Conceptual Mass\Mass.rft”);

Leave a reply to harrymattison Cancel reply