Adaptive Component Creation

Here is some code that shows how to place adaptive components. In this case, the adaptive component contains 2 placement points. I wrote this to examine the shape of an element’s bounding box – you may find other uses for it.

bboxpublic void bbox()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;   
    
    // select an element
    Element element = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element));
    
    // get bounding box of the element
    BoundingBoxXYZ bbox = element.get_BoundingBox(null);
    
    // get adaptive component family instance
    FamilySymbol fs = new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol)).Cast<FamilySymbol>().First(q => q.Name == "Type 1" && q.Family.Name == "TwoPoint");
    
    // create instances of the adaptive component
    placeInstance(fs, bbox.Min, bbox.Max, "Space Diagonal");
    placeInstance(fs, bbox.Min, new XYZ(bbox.Max.X, bbox.Max.Y, bbox.Min.Z), "Base diagonal");
    placeInstance(fs, bbox.Min, new XYZ(bbox.Max.X, bbox.Min.Y, bbox.Min.Z), "vx");
    placeInstance(fs, bbox.Min, new XYZ(bbox.Min.X, bbox.Max.Y, bbox.Min.Z), "vy");
    placeInstance(fs, bbox.Min, new XYZ(bbox.Min.X, bbox.Min.Y, bbox.Max.Z), "vz");
}

// create a new instance of the family & set the location of endpoints and Comments parameter
private void placeInstance (FamilySymbol fs, XYZ xyz1, XYZ xyz2, string comment)
{
    Document doc = fs.Document;
    using (Transaction t = new Transaction(doc,"adaptive component"))
    {
        t.Start();
        // place the adaptive component - adaptive points are automatically given some default position during creation
        FamilyInstance fi = AdaptiveComponentInstanceUtils.CreateAdaptiveComponentInstance(doc, fs);
        
        // get references to the adaptive points in the newly created instance
        IList<FamilyPointPlacementReference> refs = fi.GetFamilyPointPlacementReferences();        
        
        // adaptive component points are named "start" and "end"
        // move these points to the input XYZ locations xyz1 and xyz2 
        movePoint(doc, refs.First(q => q.Name == "start"), xyz1);
        movePoint(doc, refs.First(q => q.Name == "end"), xyz2);
        
        // get Comments parameter and set its value
        fi.get_Parameter("Comments").Set(comment);
        t.Commit();
    }
}

// move an FamilyPointPlacementReference to a specified XYZ
private void movePoint(Document doc, FamilyPointPlacementReference point, XYZ xyz)
{
    // need to subtract the default XYZ location of the point from the desired XYZ location
    ElementTransformUtils.MoveElement(doc, point.PointReference.ElementId, xyz.Subtract(locationFromPoint(doc, point)));
}

// get the XYZ location of a FamilyPointPlacementReference
private XYZ locationFromPoint(Document doc, FamilyPointPlacementReference point)
{
    Element e = doc.GetElement(point.PointReference.ElementId);
    ReferencePoint rp = e as ReferencePoint;
    return rp.Position;
}

Automating the Building Maker workflow

Tobias suggested that it would be great to see the API used to create Walls by Face, Floors by Face, Roofs by Face, and Mass Floors.

Fortunately, the Revit API has FaceWall.Create and MassInstanceUtils.AddMassLevelDataToMassInstance methods.
Unfortunately, the Revit API has no methods to create Floors by Face and Roofs by Face.

Below is a code sample showing how to create Face Walls on the non-horizontal surfaces and Mass Floors for all levels.

public void CreateFaceWallsAndMassFloors()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;
    FamilyInstance fi = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element)) as FamilyInstance;  

    WallType wType = new FilteredElementCollector(doc).OfClass(typeof(WallType)).Cast<WallType>().FirstOrDefault(q => q.Name == "Generic - 6\" Masonry");

    Options opt = new Options();
    opt.ComputeReferences = true;

    using (Transaction t = new Transaction(doc, "Create Face Walls & Mass Floors"))
    {
        t.Start();
        foreach(Solid solid in fi.get_Geometry(opt).Where(q => q is Solid).Cast<Solid>() )
          {
            foreach(Face f in solid.Faces)
              {
                  if (!FaceWall.IsValidFaceReferenceForFaceWall(doc, f.Reference))
                      continue;
                FaceWall.Create( doc, wType.Id, WallLocationLine.CoreExterior, f.Reference );

              }
          }

        foreach (Level level in new FilteredElementCollector(doc).OfClass(typeof(Level)).Cast<Level>())
        {
            MassInstanceUtils.AddMassLevelDataToMassInstance(doc, fi.Id, level.Id);
        }

      t.Commit();
    }
}

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);
}