Sunday 7 August 2011

Room Floors

Joe Stott recently posted about using Ceilings to model floor finishes in Revit, because the Automatic Ceiling tool finds the ceiling boundaries, er, automatically.

But it would be better BIM if the floor finishes were modelled as Floors. And I’ve always though there should be some way of connecting a Room’s Floor Finish property with the modelled floor finish.

So, as a proof of concept, here is a VSTA macro for R2012: MakeFloorsForSelectedRooms.

For each Room you’ve selected, it makes a new floor the same shape as the Room. The floor type is taken from the Room’s Floor Finish property:

  • If there’s a Floor Type with a matching name, the new floor will be of that type.
  • If there isn’t a matching Floor Type, a new Type is created.
  • If the Floor Finish property is blank, the new Floor will be a new default type.

As a proof of concept, it’s still a bit rough around the edges. And there are lots of ways in which it could be extended. As usual, the code comes with no warranties: Experiment at your own risk.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Autodesk.Revit.DB;
using DBArch = Autodesk.Revit.DB.Architecture;

namespace RoomFloors
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
[Autodesk.Revit.VSTA.AddInId("039bfb1c-5582-4afc-ab85-491abcf2484a")]
public partial class ThisApplication
{
#region Fields

public int MakeOpeningExceptionCount { get; set; }
public int MakeFloorExceptionCount { get; set; }

private FloorType defaultFloorType;

#endregion

#region Startup and Shutdown

private void Module_Startup(object sender, EventArgs e)
{

}

private void Module_Shutdown(object sender, EventArgs e)
{

}

#endregion

#region VSTA generated code
private void InternalStartup()
{
this.Startup += new System.EventHandler(Module_Startup);
this.Shutdown += new System.EventHandler(Module_Shutdown);
}
#endregion

public void MakeFloorsForSelectedRooms()
{
Transaction transaction = new Transaction(ActiveUIDocument.Document);
if (transaction.Start("Create new floors") == TransactionStatus.Started)
{

defaultFloorType = GetDefaultFloorType();
Autodesk.Revit.UI.Selection.SelElementSet collection = ActiveUIDocument.Selection.Elements;
foreach (Autodesk.Revit.DB.Element element in collection)
{
if (element is DBArch.Room)
{
MakeFloorForRoom(element as DBArch.Room);
}
}
transaction.Commit();
}

}

#region Floor Making

private void MakeFloorForRoom(DBArch.Room room)
{
SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
IList<IList<BoundarySegment>> bounds = room.GetBoundarySegments(options);
CurveArrArray boundsArray = new CurveArrArray();
Floor floor = null;
bool outerLoop = true;
foreach (IList<BoundarySegment> loop in bounds)
{
CurveArray profile = new CurveArray();
foreach (BoundarySegment segment in loop)
{
Curve curve = segment.Curve;
profile.Append(curve);
}
if (outerLoop)
{
FloorType newFloorType;
string newFloorTypeName = GetFloorTypeName(room);
if ("" == newFloorTypeName)
{ newFloorType = defaultFloorType; }
else { newFloorType = GetOrMakeFloorType(newFloorTypeName); }

floor = MakeAFloor(profile, room.Level, room.BaseOffset, newFloorType);
outerLoop = false;
}
else
{
if (null != floor) { MakeAnOpening(floor, profile); }
}
}
}

private string GetFloorTypeName(DBArch.Room room)
{
string newFloorTypeName = GetProperty(room, BuiltInParameter.ROOM_FINISH_FLOOR);
if (null == newFloorTypeName) { newFloorTypeName = ""; }
return newFloorTypeName;
}

private Floor MakeAFloor(CurveArray loop, Level level, double offset, FloorType newFloorType)
{
Floor newFloor = null;

try
{
newFloor = ActiveUIDocument.Document.Create.NewFloor(loop, newFloorType, level, false);

}
catch (Exception ex)
{
MakeFloorExceptionCount++;
Debug.WriteLine("Exception: " + ex.Message);
//throw;
}

if (null != newFloor)
{
BuiltInParameter paraIndex = BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM;
Parameter parameter = newFloor.get_Parameter(paraIndex);
parameter.Set(offset + GetFloorThickness(defaultFloorType));
}
return newFloor;
}

private void MakeAnOpening(Floor floor, CurveArray loop)
{
try
{
Opening newOpening = ActiveUIDocument.Document.Create.NewOpening(floor, loop, true);
}
catch (Exception ex)
{
MakeOpeningExceptionCount++;
Debug.WriteLine("Exception: " + ex.Message);
//throw;
}

}

#endregion

#region Floor Types


private FloorType GetDefaultFloorType()
{
string newFloorTypeName = GetUniqueFloorTypeName();
return MakeNewFloorType(newFloorTypeName);
}

private FloorType MakeNewFloorType(string newFloorTypeName)
{
FloorType newFloortype = (FloorType)GetFloorType().Duplicate(newFloorTypeName);
return newFloortype;
}

private string GetUniqueFloorTypeName()
{
int index = 0;
string proposedFloorTypeName;
do
{
index++;
proposedFloorTypeName = "DefaultFloorType" + index.ToString();
} while (!IsUniqueName(proposedFloorTypeName));
return proposedFloorTypeName;
}

private bool IsUniqueName(string proposedFloorTypeName)
{
FloorTypeSet floorTypes = ActiveUIDocument.Document.FloorTypes;
bool isUniqueName = true;
foreach (FloorType floorType in floorTypes)
{
if (floorType.Name == proposedFloorTypeName) { isUniqueName = false; }
}
return isUniqueName;
}

private FloorType GetFloorType()
{
FloorType myFloorType = null;
FloorTypeSet floorTypes = ActiveUIDocument.Document.FloorTypes;
foreach (FloorType floorType in floorTypes)
{
myFloorType = floorType;
}
if (null == myFloorType)
{ throw new Exception("No FloorTypes in Document"); }
return myFloorType;
}

private FloorType GetOrMakeFloorType(string floorTypeName)
{
FloorType myFloorType = null;
FloorTypeSet floorTypes = ActiveUIDocument.Document.FloorTypes;
foreach (FloorType floorType in floorTypes)
{
if (floorTypeName == floorType.Name)
{ myFloorType = floorType; }
}
if (null == myFloorType)
{ myFloorType = MakeNewFloorType(floorTypeName); }
return myFloorType;
}



private double GetFloorThickness(FloorType floorType)
{
//return 1; //in feet;
double floorThickness = 0;
CompoundStructure comStruct = floorType.GetCompoundStructure();
foreach (CompoundStructureLayer structLayer in comStruct.GetLayers())
{ floorThickness += structLayer.Width; }

return floorThickness;
}


#endregion

#region Utility

public String GetProperty(DBArch.Room room, BuiltInParameter paramEnum)
{
String propertyValue = null; //the value of parameter

//get the parameter via the parameterId
Parameter param = room.get_Parameter(paramEnum);
//get the parameter's storage type
StorageType storageType = param.StorageType;
switch (storageType)
{
case StorageType.Integer:
int iVal = param.AsInteger();
propertyValue = iVal.ToString();
break;
case StorageType.String:
String stringVal = param.AsString();
propertyValue = stringVal;
break;
case StorageType.Double:
Double dVal = param.AsDouble();
dVal = Math.Round(dVal, 2);
propertyValue = dVal.ToString();
break;
default:
break;
}
return propertyValue;
}

#endregion
}
}

4 comments:

  1. Hello, Nice macro ideea :)

    Anyway i have problems to test it

    on line 14 you have:

    -public partial class ThisApplication-

    so i have this error

    Error 1 RoomFloors.ThisApplication does not contain a definition for Startup and no extension method Startup accepting a first argument of type RoomFloors.ThisApplication could be found (are you missing a using directive or an assembly) reference?)

    Ive changed to

    -public partial class ThisDocument-

    so the error dissapeared.

    Anyway there are another 7 identical errors that say

    Error 1 The name ActiveUIDocument does not exist in the current context

    what should we do here?

    thx a lot

    ReplyDelete
  2. ah...sorry,it works :)
    your code is correct :)

    ReplyDelete
  3. Replies
    1. I'm also interested in using this code. How do i apply it?

      Delete